Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(106)

Side by Side Diff: sync/tools/testserver/sync_testserver.py

Issue 11971025: [sync] Divorce python sync test server chromiumsync.py from testserver.py (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sync/tools/testserver/run_sync_testserver.cc ('k') | sync/tools/testserver/xmppserver.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """This is a python sync server used for testing Chrome Sync.
7
8 By default, it listens on an ephemeral port and xmpp_port and sends the port
9 numbers back to the originating process over a pipe. The originating process can
10 specify an explicit port and xmpp_port if necessary.
11 """
12
13 import asyncore
14 import BaseHTTPServer
15 import errno
16 import os
17 import select
18 import socket
19 import sys
20 import urlparse
21
22 import chromiumsync
23 import echo_message
24 import testserver_base
25 import xmppserver
26
27
28 class SyncHTTPServer(testserver_base.ClientRestrictingServerMixIn,
29 testserver_base.BrokenPipeHandlerMixIn,
30 testserver_base.StoppableHTTPServer):
31 """An HTTP server that handles sync commands."""
32
33 def __init__(self, server_address, xmpp_port, request_handler_class):
34 testserver_base.StoppableHTTPServer.__init__(self,
35 server_address,
36 request_handler_class)
37 self._sync_handler = chromiumsync.TestServer()
38 self._xmpp_socket_map = {}
39 self._xmpp_server = xmppserver.XmppServer(
40 self._xmpp_socket_map, ('localhost', xmpp_port))
41 self.xmpp_port = self._xmpp_server.getsockname()[1]
42 self.authenticated = True
43
44 def GetXmppServer(self):
45 return self._xmpp_server
46
47 def HandleCommand(self, query, raw_request):
48 return self._sync_handler.HandleCommand(query, raw_request)
49
50 def HandleRequestNoBlock(self):
51 """Handles a single request.
52
53 Copied from SocketServer._handle_request_noblock().
54 """
55
56 try:
57 request, client_address = self.get_request()
58 except socket.error:
59 return
60 if self.verify_request(request, client_address):
61 try:
62 self.process_request(request, client_address)
63 except Exception:
64 self.handle_error(request, client_address)
65 self.close_request(request)
66
67 def SetAuthenticated(self, auth_valid):
68 self.authenticated = auth_valid
69
70 def GetAuthenticated(self):
71 return self.authenticated
72
73 def serve_forever(self):
74 """This is a merge of asyncore.loop() and SocketServer.serve_forever().
75 """
76
77 def HandleXmppSocket(fd, socket_map, handler):
78 """Runs the handler for the xmpp connection for fd.
79
80 Adapted from asyncore.read() et al.
81 """
82
83 xmpp_connection = socket_map.get(fd)
84 # This could happen if a previous handler call caused fd to get
85 # removed from socket_map.
86 if xmpp_connection is None:
87 return
88 try:
89 handler(xmpp_connection)
90 except (asyncore.ExitNow, KeyboardInterrupt, SystemExit):
91 raise
92 except:
93 xmpp_connection.handle_error()
94
95 while True:
96 read_fds = [ self.fileno() ]
97 write_fds = []
98 exceptional_fds = []
99
100 for fd, xmpp_connection in self._xmpp_socket_map.items():
101 is_r = xmpp_connection.readable()
102 is_w = xmpp_connection.writable()
103 if is_r:
104 read_fds.append(fd)
105 if is_w:
106 write_fds.append(fd)
107 if is_r or is_w:
108 exceptional_fds.append(fd)
109
110 try:
111 read_fds, write_fds, exceptional_fds = (
112 select.select(read_fds, write_fds, exceptional_fds))
113 except select.error, err:
114 if err.args[0] != errno.EINTR:
115 raise
116 else:
117 continue
118
119 for fd in read_fds:
120 if fd == self.fileno():
121 self.HandleRequestNoBlock()
122 continue
123 HandleXmppSocket(fd, self._xmpp_socket_map,
124 asyncore.dispatcher.handle_read_event)
125
126 for fd in write_fds:
127 HandleXmppSocket(fd, self._xmpp_socket_map,
128 asyncore.dispatcher.handle_write_event)
129
130 for fd in exceptional_fds:
131 HandleXmppSocket(fd, self._xmpp_socket_map,
132 asyncore.dispatcher.handle_expt_event)
133
134
135 class SyncPageHandler(testserver_base.BasePageHandler):
136 """Handler for the main HTTP sync server."""
137
138 def __init__(self, request, client_address, sync_http_server):
139 get_handlers = [self.ChromiumSyncTimeHandler,
140 self.ChromiumSyncMigrationOpHandler,
141 self.ChromiumSyncCredHandler,
142 self.ChromiumSyncXmppCredHandler,
143 self.ChromiumSyncDisableNotificationsOpHandler,
144 self.ChromiumSyncEnableNotificationsOpHandler,
145 self.ChromiumSyncSendNotificationOpHandler,
146 self.ChromiumSyncBirthdayErrorOpHandler,
147 self.ChromiumSyncTransientErrorOpHandler,
148 self.ChromiumSyncErrorOpHandler,
149 self.ChromiumSyncSyncTabFaviconsOpHandler,
150 self.ChromiumSyncCreateSyncedBookmarksOpHandler,
151 self.ChromiumSyncEnableKeystoreEncryptionOpHandler,
152 self.ChromiumSyncRotateKeystoreKeysOpHandler]
153
154 post_handlers = [self.ChromiumSyncCommandHandler,
155 self.ChromiumSyncTimeHandler]
156 testserver_base.BasePageHandler.__init__(self, request, client_address,
157 sync_http_server, [], get_handlers,
158 [], post_handlers, [])
159
160
161 def ChromiumSyncTimeHandler(self):
162 """Handle Chromium sync .../time requests.
163
164 The syncer sometimes checks server reachability by examining /time.
165 """
166
167 test_name = "/chromiumsync/time"
168 if not self._ShouldHandleRequest(test_name):
169 return False
170
171 # Chrome hates it if we send a response before reading the request.
172 if self.headers.getheader('content-length'):
173 length = int(self.headers.getheader('content-length'))
174 _raw_request = self.rfile.read(length)
175
176 self.send_response(200)
177 self.send_header('Content-Type', 'text/plain')
178 self.end_headers()
179 self.wfile.write('0123456789')
180 return True
181
182 def ChromiumSyncCommandHandler(self):
183 """Handle a chromiumsync command arriving via http.
184
185 This covers all sync protocol commands: authentication, getupdates, and
186 commit.
187 """
188
189 test_name = "/chromiumsync/command"
190 if not self._ShouldHandleRequest(test_name):
191 return False
192
193 length = int(self.headers.getheader('content-length'))
194 raw_request = self.rfile.read(length)
195 http_response = 200
196 raw_reply = None
197 if not self.server.GetAuthenticated():
198 http_response = 401
199 challenge = 'GoogleLogin realm="http://%s", service="chromiumsync"' % (
200 self.server.server_address[0])
201 else:
202 http_response, raw_reply = self.server.HandleCommand(
203 self.path, raw_request)
204
205 ### Now send the response to the client. ###
206 self.send_response(http_response)
207 if http_response == 401:
208 self.send_header('www-Authenticate', challenge)
209 self.end_headers()
210 self.wfile.write(raw_reply)
211 return True
212
213 def ChromiumSyncMigrationOpHandler(self):
214 test_name = "/chromiumsync/migrate"
215 if not self._ShouldHandleRequest(test_name):
216 return False
217
218 http_response, raw_reply = self.server._sync_handler.HandleMigrate(
219 self.path)
220 self.send_response(http_response)
221 self.send_header('Content-Type', 'text/html')
222 self.send_header('Content-Length', len(raw_reply))
223 self.end_headers()
224 self.wfile.write(raw_reply)
225 return True
226
227 def ChromiumSyncCredHandler(self):
228 test_name = "/chromiumsync/cred"
229 if not self._ShouldHandleRequest(test_name):
230 return False
231 try:
232 query = urlparse.urlparse(self.path)[4]
233 cred_valid = urlparse.parse_qs(query)['valid']
234 if cred_valid[0] == 'True':
235 self.server.SetAuthenticated(True)
236 else:
237 self.server.SetAuthenticated(False)
238 except Exception:
239 self.server.SetAuthenticated(False)
240
241 http_response = 200
242 raw_reply = 'Authenticated: %s ' % self.server.GetAuthenticated()
243 self.send_response(http_response)
244 self.send_header('Content-Type', 'text/html')
245 self.send_header('Content-Length', len(raw_reply))
246 self.end_headers()
247 self.wfile.write(raw_reply)
248 return True
249
250 def ChromiumSyncXmppCredHandler(self):
251 test_name = "/chromiumsync/xmppcred"
252 if not self._ShouldHandleRequest(test_name):
253 return False
254 xmpp_server = self.server.GetXmppServer()
255 try:
256 query = urlparse.urlparse(self.path)[4]
257 cred_valid = urlparse.parse_qs(query)['valid']
258 if cred_valid[0] == 'True':
259 xmpp_server.SetAuthenticated(True)
260 else:
261 xmpp_server.SetAuthenticated(False)
262 except:
263 xmpp_server.SetAuthenticated(False)
264
265 http_response = 200
266 raw_reply = 'XMPP Authenticated: %s ' % xmpp_server.GetAuthenticated()
267 self.send_response(http_response)
268 self.send_header('Content-Type', 'text/html')
269 self.send_header('Content-Length', len(raw_reply))
270 self.end_headers()
271 self.wfile.write(raw_reply)
272 return True
273
274 def ChromiumSyncDisableNotificationsOpHandler(self):
275 test_name = "/chromiumsync/disablenotifications"
276 if not self._ShouldHandleRequest(test_name):
277 return False
278 self.server.GetXmppServer().DisableNotifications()
279 result = 200
280 raw_reply = ('<html><title>Notifications disabled</title>'
281 '<H1>Notifications disabled</H1></html>')
282 self.send_response(result)
283 self.send_header('Content-Type', 'text/html')
284 self.send_header('Content-Length', len(raw_reply))
285 self.end_headers()
286 self.wfile.write(raw_reply)
287 return True
288
289 def ChromiumSyncEnableNotificationsOpHandler(self):
290 test_name = "/chromiumsync/enablenotifications"
291 if not self._ShouldHandleRequest(test_name):
292 return False
293 self.server.GetXmppServer().EnableNotifications()
294 result = 200
295 raw_reply = ('<html><title>Notifications enabled</title>'
296 '<H1>Notifications enabled</H1></html>')
297 self.send_response(result)
298 self.send_header('Content-Type', 'text/html')
299 self.send_header('Content-Length', len(raw_reply))
300 self.end_headers()
301 self.wfile.write(raw_reply)
302 return True
303
304 def ChromiumSyncSendNotificationOpHandler(self):
305 test_name = "/chromiumsync/sendnotification"
306 if not self._ShouldHandleRequest(test_name):
307 return False
308 query = urlparse.urlparse(self.path)[4]
309 query_params = urlparse.parse_qs(query)
310 channel = ''
311 data = ''
312 if 'channel' in query_params:
313 channel = query_params['channel'][0]
314 if 'data' in query_params:
315 data = query_params['data'][0]
316 self.server.GetXmppServer().SendNotification(channel, data)
317 result = 200
318 raw_reply = ('<html><title>Notification sent</title>'
319 '<H1>Notification sent with channel "%s" '
320 'and data "%s"</H1></html>'
321 % (channel, data))
322 self.send_response(result)
323 self.send_header('Content-Type', 'text/html')
324 self.send_header('Content-Length', len(raw_reply))
325 self.end_headers()
326 self.wfile.write(raw_reply)
327 return True
328
329 def ChromiumSyncBirthdayErrorOpHandler(self):
330 test_name = "/chromiumsync/birthdayerror"
331 if not self._ShouldHandleRequest(test_name):
332 return False
333 result, raw_reply = self.server._sync_handler.HandleCreateBirthdayError()
334 self.send_response(result)
335 self.send_header('Content-Type', 'text/html')
336 self.send_header('Content-Length', len(raw_reply))
337 self.end_headers()
338 self.wfile.write(raw_reply)
339 return True
340
341 def ChromiumSyncTransientErrorOpHandler(self):
342 test_name = "/chromiumsync/transienterror"
343 if not self._ShouldHandleRequest(test_name):
344 return False
345 result, raw_reply = self.server._sync_handler.HandleSetTransientError()
346 self.send_response(result)
347 self.send_header('Content-Type', 'text/html')
348 self.send_header('Content-Length', len(raw_reply))
349 self.end_headers()
350 self.wfile.write(raw_reply)
351 return True
352
353 def ChromiumSyncErrorOpHandler(self):
354 test_name = "/chromiumsync/error"
355 if not self._ShouldHandleRequest(test_name):
356 return False
357 result, raw_reply = self.server._sync_handler.HandleSetInducedError(
358 self.path)
359 self.send_response(result)
360 self.send_header('Content-Type', 'text/html')
361 self.send_header('Content-Length', len(raw_reply))
362 self.end_headers()
363 self.wfile.write(raw_reply)
364 return True
365
366 def ChromiumSyncSyncTabFaviconsOpHandler(self):
367 test_name = "/chromiumsync/synctabfavicons"
368 if not self._ShouldHandleRequest(test_name):
369 return False
370 result, raw_reply = self.server._sync_handler.HandleSetSyncTabFavicons()
371 self.send_response(result)
372 self.send_header('Content-Type', 'text/html')
373 self.send_header('Content-Length', len(raw_reply))
374 self.end_headers()
375 self.wfile.write(raw_reply)
376 return True
377
378 def ChromiumSyncCreateSyncedBookmarksOpHandler(self):
379 test_name = "/chromiumsync/createsyncedbookmarks"
380 if not self._ShouldHandleRequest(test_name):
381 return False
382 result, raw_reply = self.server._sync_handler.HandleCreateSyncedBookmarks()
383 self.send_response(result)
384 self.send_header('Content-Type', 'text/html')
385 self.send_header('Content-Length', len(raw_reply))
386 self.end_headers()
387 self.wfile.write(raw_reply)
388 return True
389
390 def ChromiumSyncEnableKeystoreEncryptionOpHandler(self):
391 test_name = "/chromiumsync/enablekeystoreencryption"
392 if not self._ShouldHandleRequest(test_name):
393 return False
394 result, raw_reply = (
395 self.server._sync_handler.HandleEnableKeystoreEncryption())
396 self.send_response(result)
397 self.send_header('Content-Type', 'text/html')
398 self.send_header('Content-Length', len(raw_reply))
399 self.end_headers()
400 self.wfile.write(raw_reply)
401 return True
402
403 def ChromiumSyncRotateKeystoreKeysOpHandler(self):
404 test_name = "/chromiumsync/rotatekeystorekeys"
405 if not self._ShouldHandleRequest(test_name):
406 return False
407 result, raw_reply = (
408 self.server._sync_handler.HandleRotateKeystoreKeys())
409 self.send_response(result)
410 self.send_header('Content-Type', 'text/html')
411 self.send_header('Content-Length', len(raw_reply))
412 self.end_headers()
413 self.wfile.write(raw_reply)
414 return True
415
416
417 class SyncServerRunner(testserver_base.TestServerRunner):
418 """TestServerRunner for the net test servers."""
419
420 def __init__(self):
421 super(SyncServerRunner, self).__init__()
422
423 def create_server(self, server_data):
424 port = self.options.port
425 host = self.options.host
426 xmpp_port = self.options.xmpp_port
427 server = SyncHTTPServer((host, port), xmpp_port, SyncPageHandler)
428 print 'Sync HTTP server started on port %d...' % server.server_port
429 print 'Sync XMPP server started on port %d...' % server.xmpp_port
430 server_data['port'] = server.server_port
431 server_data['xmpp_port'] = server.xmpp_port
432 return server
433
434 def run_server(self):
435 testserver_base.TestServerRunner.run_server(self)
436
437 def add_options(self):
438 testserver_base.TestServerRunner.add_options(self)
439 self.option_parser.add_option('--xmpp-port', default='0', type='int',
440 help='Port used by the XMPP server. If '
441 'unspecified, the XMPP server will listen on '
442 'an ephemeral port.')
443 # Override the default logfile name used in testserver.py.
444 self.option_parser.set_defaults(log_file='sync_testserver.log')
445
446 if __name__ == '__main__':
447 sys.exit(SyncServerRunner().main())
OLDNEW
« no previous file with comments | « sync/tools/testserver/run_sync_testserver.cc ('k') | sync/tools/testserver/xmppserver.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698