Skip to content

Commit 68733ae

Browse files
committed
Use tornado for a local web server if possible
1 parent 502698e commit 68733ae

File tree

5 files changed

+111
-17
lines changed

5 files changed

+111
-17
lines changed

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ RUN pip install \
5555
psutil \
5656
requests \
5757
ujson \
58+
tornado \
5859
xvfbwrapper \
5960
marionette_driver
6061

docs/install.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ wptagent currently supports Windows, Linux and OSX for desktop browsers as well
1010
* pypiwin32 (Windows only)
1111
* requests
1212
* ujson
13+
* tornado
1314
* xvfbwrapper (Linux only)
1415
* bind9utils (Linux only, for rndc)
1516
* marionette_driver (Firefox)
@@ -21,7 +22,7 @@ wptagent currently supports Windows, Linux and OSX for desktop browsers as well
2122
* cgroup-tools (Linux only if mobile CPU emulation is desired)
2223
* traceroute (Mac and Linux)
2324
* Debian:
24-
* ```sudo apt-get install -y python2.7 python-pip imagemagick ffmpeg xvfb dbus-x11 cgroup-tools traceroute && sudo pip install dnspython monotonic pillow psutil requests ujson xvfbwrapper marionette_driver```
25+
* ```sudo apt-get install -y python2.7 python-pip imagemagick ffmpeg xvfb dbus-x11 cgroup-tools traceroute && sudo pip install dnspython monotonic pillow psutil requests ujson tornado xvfbwrapper marionette_driver```
2526
* Chrome Browser
2627
* Linux stable, beta and unstable channels on Ubuntu/Debian:
2728
* ```wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -```

internal/tornado_responder.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Copyright 2017 Google Inc. All rights reserved.
2+
# Use of this source code is governed by the Apache 2.0 license that can be
3+
# found in the LICENSE file.
4+
import logging
5+
import tornado.ioloop
6+
import tornado.web
7+
8+
MESSAGE_SERVER = None
9+
10+
class TornadoRequestHandler(tornado.web.RequestHandler):
11+
"""Request handler for when we are using tornado"""
12+
def get(self):
13+
"""Handle GET requests"""
14+
import ujson as json
15+
logging.debug(self.request.uri)
16+
response = None
17+
content_type = 'text/plain'
18+
if self.request.uri == '/ping':
19+
response = 'pong'
20+
elif self.request.uri == '/config':
21+
# JSON config data
22+
content_type = 'application/json'
23+
response = '{}'
24+
if MESSAGE_SERVER.config is not None:
25+
response = json.dumps(MESSAGE_SERVER.config)
26+
elif self.request.uri == '/config.html':
27+
# HTML page that can be queried from the extension for config data
28+
content_type = 'text/html'
29+
response = "<html><head>\n"
30+
if MESSAGE_SERVER.config is not None:
31+
import cgi
32+
response += '<div id="wptagentConfig" style="display: none;">'
33+
response += cgi.escape(json.dumps(MESSAGE_SERVER.config))
34+
response += '</div>'
35+
response += "</head><body></body></html>"
36+
37+
if response is not None:
38+
self.set_status(200)
39+
self.set_header("Content-Type", content_type)
40+
self.write(response)
41+
42+
def post(self):
43+
"""Handle POST messages"""
44+
import ujson as json
45+
try:
46+
messages = self.request.body
47+
if messages is not None and len(messages):
48+
if self.request.uri == '/log':
49+
logging.debug(messages)
50+
else:
51+
for line in messages.splitlines():
52+
line = line.strip()
53+
if len(line):
54+
message = json.loads(line)
55+
if 'body' not in message and self.request.uri != '/etw':
56+
message['body'] = None
57+
MESSAGE_SERVER.handle_message(message)
58+
except Exception:
59+
pass
60+
self.set_status(200)
61+
62+
def stop_tornado():
63+
"""Stop the tornado server"""
64+
ioloop = tornado.ioloop.IOLoop.instance()
65+
ioloop.add_callback(ioloop.stop)
66+
67+
def start_tornado(message_server):
68+
"""Start (and run) the tornado server"""
69+
global MESSAGE_SERVER
70+
MESSAGE_SERVER = message_server
71+
application = tornado.web.Application([(r"/.*", TornadoRequestHandler)])
72+
application.listen(8888, '127.0.0.1')
73+
message_server.is_started.set()
74+
tornado.ioloop.IOLoop.instance().start()

ubuntu_install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/bash
22
sudo apt-get install -y python2.7 python-pip imagemagick ffmpeg xvfb dbus-x11 cgroup-tools traceroute software-properties-common python-software-properties psmisc
33
sudo dbus-uuidgen --ensure
4-
sudo pip install dnspython monotonic pillow psutil requests ujson xvfbwrapper marionette_driver
4+
sudo pip install dnspython monotonic pillow psutil requests ujson tornado xvfbwrapper marionette_driver
55
curl -sL https://deb.nodesource.com/setup_7.x | sudo bash -
66
sudo apt-get install -y nodejs
77
sudo npm install -g lighthouse

wptagent.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ def run_testing(self):
7777
if not self.options.android and not self.options.iOS:
7878
message_server = MessageServer()
7979
message_server.start()
80+
if not message_server.is_ok():
81+
logging.error("Unable to start the local message server")
82+
return
8083
while not self.must_exit:
8184
try:
8285
if os.path.isfile(exit_file):
@@ -293,6 +296,13 @@ def startup(self):
293296
"and make sure it is in the path."
294297
ret = False
295298

299+
try:
300+
import tornado as _
301+
except ImportError:
302+
from internal.os_util import run_elevated
303+
logging.debug('Trying to install tornado...')
304+
run_elevated(sys.executable, '-m pip install tornado')
305+
296306
if platform.system() == "Linux":
297307
try:
298308
subprocess.check_output(['traceroute', '--version'])
@@ -366,7 +376,6 @@ def startup(self):
366376
ret = False
367377
return ret
368378

369-
370379
class HandleMessage(BaseHTTPRequestHandler):
371380
"""Handle a single message from the extension"""
372381
# 1.1 keep-alive is pretty buggy, leave this disabled for now
@@ -480,7 +489,8 @@ def __init__(self):
480489
self.thread = None
481490
self.messages = Queue.Queue()
482491
self.config = None
483-
self.__is_started = threading.Event()
492+
self.is_started = threading.Event()
493+
self.using_tornado = False
484494

485495
def get_message(self, timeout):
486496
"""Get a single message from the queue"""
@@ -503,17 +513,21 @@ def handle_message(self, message):
503513

504514
def start(self):
505515
"""Start running the server in a background thread"""
506-
self.__is_started.clear()
516+
self.is_started.clear()
507517
self.thread = threading.Thread(target=self.run)
508518
self.thread.daemon = True
509519
self.thread.start()
510-
self.__is_started.wait(timeout=30)
520+
self.is_started.wait(timeout=30)
511521

512522
def stop(self):
513523
"""Stop running the server"""
514524
logging.debug("Shutting down extension server")
515525
self.must_exit = True
516-
if self.server is not None:
526+
if self.using_tornado:
527+
from internal.tornado_responder import stop_tornado
528+
stop_tornado()
529+
self.thread.join()
530+
elif self.server is not None:
517531
try:
518532
self.server.shutdown()
519533
except Exception:
@@ -543,18 +557,22 @@ def run(self):
543557
handler = handler_template(self)
544558
logging.debug('Starting extension server on port 8888')
545559
try:
546-
self.server = HTTPServer(('127.0.0.1', 8888), handler)
547-
self.server.timeout = 10
548-
self.__is_started.set()
549-
self.server.serve_forever()
550-
except Exception:
551-
logging.exception("Message server main loop exception")
552-
self.__is_started.set()
553-
if self.server is not None:
560+
from internal.tornado_responder import start_tornado
561+
start_tornado(self)
562+
except ImportError:
554563
try:
555-
self.server.socket.close()
564+
self.server = HTTPServer(('127.0.0.1', 8888), handler)
565+
self.server.timeout = 10
566+
self.is_started.set()
567+
self.server.serve_forever()
556568
except Exception:
557-
pass
569+
logging.exception("Message server main loop exception")
570+
self.is_started.set()
571+
if self.server is not None:
572+
try:
573+
self.server.socket.close()
574+
except Exception:
575+
pass
558576

559577
def parse_ini(ini):
560578
"""Parse an ini file and convert it to a dictionary"""

0 commit comments

Comments
 (0)