OLD | NEW |
---|---|
(Empty) | |
1 # Copyright 2015 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. | |
M-A Ruel
2016/01/15 14:07:13
You test for POST, not PUT.
Mike Meade
2016/01/18 20:25:51
Done.
| |
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_POST(self, request): | |
98 """Handles POST 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_POST, | |
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_POST(self): | |
160 """Handles POST requests.""" | |
161 handler = self._GetHandler() | |
162 if handler: | |
163 handler.do_POST(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 |