1 | # (c) 2005 Clark C. Evans |
---|
2 | # This module is part of the Python Paste Project and is released under |
---|
3 | # the MIT License: http://www.opensource.org/licenses/mit-license.php |
---|
4 | # This code was written with funding by http://prometheusresearch.com |
---|
5 | """ |
---|
6 | WSGI Test Server |
---|
7 | |
---|
8 | This builds upon paste.util.baseserver to customize it for regressions |
---|
9 | where using raw_interactive won't do. |
---|
10 | |
---|
11 | |
---|
12 | """ |
---|
13 | import time |
---|
14 | from paste.httpserver import * |
---|
15 | |
---|
16 | class WSGIRegressionServer(WSGIServer): |
---|
17 | """ |
---|
18 | A threaded WSGIServer for use in regression testing. To use this |
---|
19 | module, call serve(application, regression=True), and then call |
---|
20 | server.accept() to let it handle one request. When finished, use |
---|
21 | server.stop() to shutdown the server. Note that all pending requests |
---|
22 | are processed before the server shuts down. |
---|
23 | """ |
---|
24 | defaulttimeout = 10 |
---|
25 | def __init__ (self, *args, **kwargs): |
---|
26 | WSGIServer.__init__(self, *args, **kwargs) |
---|
27 | self.stopping = [] |
---|
28 | self.pending = [] |
---|
29 | self.timeout = self.defaulttimeout |
---|
30 | # this is a local connection, be quick |
---|
31 | self.socket.settimeout(2) |
---|
32 | def serve_forever(self): |
---|
33 | from threading import Thread |
---|
34 | thread = Thread(target=self.serve_pending) |
---|
35 | thread.start() |
---|
36 | def reset_expires(self): |
---|
37 | if self.timeout: |
---|
38 | self.expires = time.time() + self.timeout |
---|
39 | def close_request(self, *args, **kwargs): |
---|
40 | WSGIServer.close_request(self, *args, **kwargs) |
---|
41 | self.pending.pop() |
---|
42 | self.reset_expires() |
---|
43 | def serve_pending(self): |
---|
44 | self.reset_expires() |
---|
45 | while not self.stopping or self.pending: |
---|
46 | now = time.time() |
---|
47 | if now > self.expires and self.timeout: |
---|
48 | # note regression test doesn't handle exceptions in |
---|
49 | # threads very well; so we just print and exit |
---|
50 | print "\nWARNING: WSGIRegressionServer timeout exceeded\n" |
---|
51 | break |
---|
52 | if self.pending: |
---|
53 | self.handle_request() |
---|
54 | time.sleep(.1) |
---|
55 | def stop(self): |
---|
56 | """ stop the server (called from tester's thread) """ |
---|
57 | self.stopping.append(True) |
---|
58 | def accept(self, count = 1): |
---|
59 | """ accept another request (called from tester's thread) """ |
---|
60 | assert not self.stopping |
---|
61 | [self.pending.append(True) for x in range(count)] |
---|
62 | |
---|
63 | def serve(application, host=None, port=None, handler=None): |
---|
64 | server = WSGIRegressionServer(application, host, port, handler) |
---|
65 | print "serving on %s:%s" % server.server_address |
---|
66 | server.serve_forever() |
---|
67 | return server |
---|
68 | |
---|
69 | if __name__ == '__main__': |
---|
70 | import urllib |
---|
71 | from paste.wsgilib import dump_environ |
---|
72 | server = serve(dump_environ) |
---|
73 | baseuri = ("http://%s:%s" % server.server_address) |
---|
74 | |
---|
75 | def fetch(path): |
---|
76 | # tell the server to humor exactly one more request |
---|
77 | server.accept(1) |
---|
78 | # not needed; but this is what you do if the server |
---|
79 | # may not respond in a resonable time period |
---|
80 | import socket |
---|
81 | socket.setdefaulttimeout(5) |
---|
82 | # build a uri, fetch and return |
---|
83 | return urllib.urlopen(baseuri + path).read() |
---|
84 | |
---|
85 | assert "PATH_INFO: /foo" in fetch("/foo") |
---|
86 | assert "PATH_INFO: /womble" in fetch("/womble") |
---|
87 | |
---|
88 | # ok, let's make one more final request... |
---|
89 | server.accept(1) |
---|
90 | # and then schedule a stop() |
---|
91 | server.stop() |
---|
92 | # and then... fetch it... |
---|
93 | urllib.urlopen(baseuri) |
---|