OLD | NEW |
(Empty) | |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 """A server to allow events to be passed from one task to another. |
| 6 |
| 7 This server supports getting, setting, and deleting of arbitrary event names. |
| 8 This is a "pull" style of server, so its on the producer and consumer to |
| 9 proactively get, set, and delete the events. |
| 10 |
| 11 |
| 12 Operation | Url | Action |
| 13 ------------------------------------------------------------------------------- |
| 14 GET | /events/<NAME> | Returns 200 for an event that is set. |
| 15 | | Returns 404 for an event that is not set |
| 16 ------------------------------------------------------------------------------- |
| 17 PUT | /events/<NAME> | Sets the event and returns 200. |
| 18 ------------------------------------------------------------------------------- |
| 19 DELETE | /events/<NAME> | Deletes the event and returns 200. |
| 20 ------------------------------------------------------------------------------- |
| 21 |
| 22 Errors |
| 23 Error | Action | Reason |
| 24 ------------------------------------------------------------------------------- |
| 25 403 | GET | Category is not specified |
| 26 ------------------------------------------------------------------------------- |
| 27 404 | GET | Event doesn't exist or isn't set |
| 28 ------------------------------------------------------------------------------- |
| 29 501 | All | Incorrect URL format (must be events/<NAME>) |
| 30 ------------------------------------------------------------------------------- |
| 31 """ |
| 32 |
| 33 import BaseHTTPServer |
| 34 import httplib |
| 35 import re |
| 36 import SimpleHTTPServer |
| 37 import SocketServer |
| 38 import threading |
| 39 |
| 40 from legion.lib import common_lib |
| 41 |
| 42 |
| 43 class ThreadedServer(SocketServer.ThreadingMixIn, |
| 44 BaseHTTPServer.HTTPServer): |
| 45 """An extension of the HTTPServer class which handles requests in threads.""" |
| 46 |
| 47 def __init__(self, address=''): |
| 48 self._port = common_lib.GetUnusedPort() |
| 49 self._address = address |
| 50 BaseHTTPServer.HTTPServer.__init__(self, |
| 51 (self._address, self._port), |
| 52 HttpRequestHandler) |
| 53 |
| 54 @property |
| 55 def port(self): |
| 56 return self._port |
| 57 |
| 58 @property |
| 59 def address(self): |
| 60 return self._address |
| 61 |
| 62 def start(self): |
| 63 """Starts the server in another thread. |
| 64 |
| 65 The thread will stay active until shutdown() is called. There is no reason |
| 66 to hold a reference to the thread object. |
| 67 """ |
| 68 threading.Thread(target=self.serve_forever).start() |
| 69 |
| 70 |
| 71 class EventHandler(object): |
| 72 """Handles event set/get/delete operations.""" |
| 73 |
| 74 _REGEX = re.compile('^/events/(?P<name>[a-zA-Z0-9]*)') |
| 75 _events = {} |
| 76 _event_lock = threading.Lock() |
| 77 |
| 78 def _GetName(self, request): |
| 79 """Gets the event name from the URL.""" |
| 80 match = self._REGEX.match(request.path) |
| 81 if not match: |
| 82 return None |
| 83 return match.group('name') |
| 84 |
| 85 |
| 86 def do_GET(self, request): |
| 87 """Handles GET requests.""" |
| 88 name = self._GetName(request) |
| 89 if not name: |
| 90 return request.send_error(501, 'Event name required') |
| 91 with self._event_lock: |
| 92 if name not in self._events: |
| 93 return request.send_error(404, 'Event: %s not found' % name) |
| 94 else: |
| 95 return request.send_response(200) |
| 96 |
| 97 def do_PUT(self, request): |
| 98 """Handles PUT requests.""" |
| 99 name = self._GetName(request) |
| 100 if not name: |
| 101 return request.send_error(501, 'Event name required') |
| 102 with self._event_lock: |
| 103 self._events[name] = 1 |
| 104 return request.send_response(200) |
| 105 |
| 106 def do_DELETE(self, request): |
| 107 """Handles DELETE requests.""" |
| 108 name = self._GetName(request) |
| 109 if not name: |
| 110 return request.send_error(501, 'Event name required') |
| 111 with self._event_lock: |
| 112 if name in self._events: |
| 113 del self._events[name] |
| 114 return request.send_response(200) |
| 115 else: |
| 116 return request.send_error(404, 'Event not found') |
| 117 |
| 118 |
| 119 class HttpRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): |
| 120 """Request handler which dispatches requests to the correct subhandler. |
| 121 |
| 122 To extend this functionality implement a class that handles do_GET, do_PUT, |
| 123 and do_DELETE methods, then add the name/class to the _HANDLERS dict. |
| 124 """ |
| 125 |
| 126 _REGEX = re.compile('/(?P<category>[a-zA-Z0-9]*)') |
| 127 |
| 128 _HANDLERS = { |
| 129 'events': EventHandler, |
| 130 } |
| 131 |
| 132 def log_message(self, *args, **kwargs): |
| 133 """Silence those pesky server-side print statements.""" |
| 134 pass |
| 135 |
| 136 def _GetCategoryName(self): |
| 137 """Extracts and returns the category name.""" |
| 138 match = self._REGEX.match(self.path) |
| 139 if not match: |
| 140 return |
| 141 return match.group('category') |
| 142 |
| 143 def _GetHandler(self): |
| 144 """Returns the category handler object if it exists.""" |
| 145 category = self._GetCategoryName() |
| 146 if not category: |
| 147 return self.send_error(403, 'Category must be supplied') |
| 148 handler = self._HANDLERS.get(category) |
| 149 if not handler: |
| 150 return self.send_error(501, '/%s is not supported' % category) |
| 151 return handler() |
| 152 |
| 153 def do_GET(self): |
| 154 """Handles GET requests.""" |
| 155 handler = self._GetHandler() |
| 156 if handler: |
| 157 handler.do_GET(self) |
| 158 |
| 159 def do_PUT(self): |
| 160 """Handles PUT requests.""" |
| 161 handler = self._GetHandler() |
| 162 if handler: |
| 163 handler.do_PUT(self) |
| 164 |
| 165 def do_DELETE(self): |
| 166 """Handles DELETE requests.""" |
| 167 handler = self._GetHandler() |
| 168 if handler: |
| 169 handler.do_DELETE(self) |
OLD | NEW |