OLD | NEW |
(Empty) | |
| 1 import logging |
| 2 import os |
| 3 import select |
| 4 import SimpleHTTPServer |
| 5 import SocketServer |
| 6 import threading |
| 7 |
| 8 HERE = os.path.dirname(__file__) |
| 9 logger = logging.getLogger(__name__) |
| 10 |
| 11 |
| 12 class ThisDirHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): |
| 13 def translate_path(self, path): |
| 14 path = path.split('?', 1)[0].split('#', 1)[0] |
| 15 return os.path.join(HERE, *filter(None, path.split('/'))) |
| 16 |
| 17 def log_message(self, s, *args): |
| 18 # output via logging so nose can catch it |
| 19 logger.info(s, *args) |
| 20 |
| 21 |
| 22 class ShutdownServer(SocketServer.TCPServer): |
| 23 """Mixin that allows serve_forever to be shut down. |
| 24 |
| 25 The methods in this mixin are backported from SocketServer.py in the Python |
| 26 2.6.4 standard library. The mixin is unnecessary in 2.6 and later, when |
| 27 BaseServer supports the shutdown method directly. |
| 28 """ |
| 29 |
| 30 def __init__(self, *args, **kwargs): |
| 31 SocketServer.TCPServer.__init__(self, *args, **kwargs) |
| 32 self.__is_shut_down = threading.Event() |
| 33 self.__serving = False |
| 34 |
| 35 def serve_forever(self, poll_interval=0.1): |
| 36 """Handle one request at a time until shutdown. |
| 37 |
| 38 Polls for shutdown every poll_interval seconds. Ignores |
| 39 self.timeout. If you need to do periodic tasks, do them in |
| 40 another thread. |
| 41 """ |
| 42 self.__serving = True |
| 43 self.__is_shut_down.clear() |
| 44 while self.__serving: |
| 45 r, w, e = select.select([self.socket], [], [], poll_interval) |
| 46 if r: |
| 47 self._handle_request_noblock() |
| 48 self.__is_shut_down.set() |
| 49 |
| 50 def shutdown(self): |
| 51 """Stops the serve_forever loop. |
| 52 |
| 53 Blocks until the loop has finished. This must be called while |
| 54 serve_forever() is running in another thread, or it will deadlock. |
| 55 """ |
| 56 self.__serving = False |
| 57 self.__is_shut_down.wait() |
| 58 |
| 59 def handle_request(self): |
| 60 """Handle one request, possibly blocking. |
| 61 |
| 62 Respects self.timeout. |
| 63 """ |
| 64 # Support people who used socket.settimeout() to escape |
| 65 # handle_request before self.timeout was available. |
| 66 timeout = self.socket.gettimeout() |
| 67 if timeout is None: |
| 68 timeout = self.timeout |
| 69 elif self.timeout is not None: |
| 70 timeout = min(timeout, self.timeout) |
| 71 fd_sets = select.select([self], [], [], timeout) |
| 72 if not fd_sets[0]: |
| 73 self.handle_timeout() |
| 74 return |
| 75 self._handle_request_noblock() |
| 76 |
| 77 def _handle_request_noblock(self): |
| 78 """Handle one request, without blocking. |
| 79 |
| 80 I assume that select.select has returned that the socket is |
| 81 readable before this function was called, so there should be |
| 82 no risk of blocking in get_request(). |
| 83 """ |
| 84 try: |
| 85 request, client_address = self.get_request() |
| 86 except socket.error: |
| 87 return |
| 88 if self.verify_request(request, client_address): |
| 89 try: |
| 90 self.process_request(request, client_address) |
| 91 except: |
| 92 self.handle_error(request, client_address) |
| 93 self.close_request(request) |
| 94 |
| 95 |
| 96 def start_server(handler): |
| 97 httpd = ShutdownServer(("", 0), handler) |
| 98 threading.Thread(target=httpd.serve_forever).start() |
| 99 _, port = httpd.socket.getsockname() |
| 100 return httpd, port |
OLD | NEW |