1 """Adapt an HTTP server."""
2
3 import time
4
5
7 """Adapter for an HTTP server.
8
9 If you need to start more than one HTTP server (to serve on multiple
10 ports, or protocols, etc.), you can manually register each one and then
11 start them all with bus.start:
12
13 s1 = ServerAdapter(bus, MyWSGIServer(host='0.0.0.0', port=80))
14 s2 = ServerAdapter(bus, another.HTTPServer(host='127.0.0.1', SSL=True))
15 s1.subscribe()
16 s2.subscribe()
17 bus.start()
18 """
19
20 - def __init__(self, bus, httpserver=None, bind_addr=None):
26
30
34
36 """Start the HTTP server."""
37 if isinstance(self.bind_addr, tuple):
38 host, port = self.bind_addr
39 on_what = "%s:%s" % (host, port)
40 else:
41 on_what = "socket file: %s" % self.bind_addr
42
43 if self.running:
44 self.bus.log("Already serving on %s" % on_what)
45 return
46
47 self.interrupt = None
48 if not self.httpserver:
49 raise ValueError("No HTTP server has been created.")
50
51
52 if isinstance(self.bind_addr, tuple):
53 wait_for_free_port(*self.bind_addr)
54
55 import threading
56 t = threading.Thread(target=self._start_http_thread)
57 t.setName("HTTPServer " + t.getName())
58 t.start()
59
60 self.wait()
61 self.running = True
62 self.bus.log("Serving on %s" % on_what)
63 start.priority = 75
64
66 """HTTP servers MUST be running in new threads, so that the
67 main thread persists to receive KeyboardInterrupt's. If an
68 exception is raised in the httpserver's thread then it's
69 trapped here, and the bus (and therefore our httpserver)
70 are shut down.
71 """
72 try:
73 self.httpserver.start()
74 except KeyboardInterrupt, exc:
75 self.bus.log("<Ctrl-C> hit: shutting down HTTP server")
76 self.interrupt = exc
77 self.bus.exit()
78 except SystemExit, exc:
79 self.bus.log("SystemExit raised: shutting down HTTP server")
80 self.interrupt = exc
81 self.bus.exit()
82 raise
83 except:
84 import sys
85 self.interrupt = sys.exc_info()[1]
86 self.bus.log("Error in HTTP server: shutting down",
87 traceback=True, level=40)
88 self.bus.exit()
89 raise
90
102
104 """Stop the HTTP server."""
105 if self.running:
106
107 self.httpserver.stop()
108
109 if isinstance(self.bind_addr, tuple):
110 wait_for_free_port(*self.bind_addr)
111 self.running = False
112 self.bus.log("HTTP Server %s shut down" % self.httpserver)
113 else:
114 self.bus.log("HTTP Server %s already shut down" % self.httpserver)
115 stop.priority = 25
116
118 """Restart the HTTP server."""
119 self.stop()
120 self.start()
121
122
124 """Adapter for a flup.server.fcgi.WSGIServer."""
125
130
132 """Start the FCGI server."""
133
134
135 from flup.server.fcgi import WSGIServer
136 self.fcgiserver = WSGIServer(*self.args, **self.kwargs)
137
138
139
140
141
142
143
144
145
146 self.fcgiserver._installSignalHandlers = lambda: None
147 self.fcgiserver._oldSIGs = []
148 self.ready = True
149 self.fcgiserver.run()
150
152 """Stop the HTTP server."""
153
154 self.fcgiserver._keepGoing = False
155
156 self.fcgiserver._threadPool.maxSpare = self.fcgiserver._threadPool._idleCount
157 self.ready = False
158
159
161 """Adapter for a flup.server.scgi.WSGIServer."""
162
167
169 """Start the SCGI server."""
170
171
172 from flup.server.scgi import WSGIServer
173 self.scgiserver = WSGIServer(*self.args, **self.kwargs)
174
175
176
177
178
179
180
181
182
183 self.scgiserver._installSignalHandlers = lambda: None
184 self.scgiserver._oldSIGs = []
185 self.ready = True
186 self.scgiserver.run()
187
189 """Stop the HTTP server."""
190 self.ready = False
191
192 self.scgiserver._keepGoing = False
193
194 self.scgiserver._threadPool.maxSpare = 0
195
196
198 """Return the host on which a client can connect to the given listener."""
199 if server_host == '0.0.0.0':
200
201 return '127.0.0.1'
202 if server_host == '::':
203
204 return '::1'
205 return server_host
206
208 """Raise an error if the given port is not free on the given host."""
209 if not host:
210 raise ValueError("Host values of '' or None are not allowed.")
211 host = client_host(host)
212 port = int(port)
213
214 import socket
215
216
217
218 for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
219 socket.SOCK_STREAM):
220 af, socktype, proto, canonname, sa = res
221 s = None
222 try:
223 s = socket.socket(af, socktype, proto)
224
225
226 s.settimeout(timeout)
227 s.connect((host, port))
228 s.close()
229 raise IOError("Port %s is in use on %s; perhaps the previous "
230 "httpserver did not shut down properly." %
231 (repr(port), repr(host)))
232 except socket.error:
233 if s:
234 s.close()
235
237 """Wait for the specified port to become free (drop requests)."""
238 if not host:
239 raise ValueError("Host values of '' or None are not allowed.")
240
241 for trial in xrange(50):
242 try:
243
244 check_port(host, port, timeout=0.1)
245 except IOError:
246
247 time.sleep(0.1)
248 else:
249 return
250
251 raise IOError("Port %r not free on %r" % (port, host))
252
254 """Wait for the specified port to become active (receive requests)."""
255 if not host:
256 raise ValueError("Host values of '' or None are not allowed.")
257
258 for trial in xrange(50):
259 try:
260 check_port(host, port)
261 except IOError:
262 return
263 else:
264 time.sleep(.1)
265
266 raise IOError("Port %r not bound on %r" % (port, host))
267