mirror of https://github.com/vitalif/phantomjs
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py, the embedded HTTP server now supports scripts in test/www/. If you try to load http://localhost:9180/path and test/www/path doesn't exist but test/www/path.py does, then test/www/path.py is loaded as a module. That module must export one function, handle_request(), which is called to produce the response. handle_request() has the same semantics as SimpleHTTPRequestHandler.send_head(). That is, it takes one argument, the SimpleHTTPRequestHandler object, conventionally named 'req'. It should call the send_response(), send_header(), and end_headers() methods of that object as appropriate. And it should return a readable filelike whose contents are taken as the body of the response. If either module import or handle_request() throws any exception, the HTTP response will be a 500 Internal Server Error with body provided by cgitb. Ongoing work on issue #12439.2.0
parent
2b2d52e343
commit
4d60e9450d
|
@ -1,6 +1,8 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import cStringIO as StringIO
|
||||||
import glob
|
import glob
|
||||||
|
import imp
|
||||||
import json
|
import json
|
||||||
import optparse
|
import optparse
|
||||||
import os
|
import os
|
||||||
|
@ -8,6 +10,7 @@ import posixpath
|
||||||
import SimpleHTTPServer
|
import SimpleHTTPServer
|
||||||
import SocketServer
|
import SocketServer
|
||||||
import socket
|
import socket
|
||||||
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
|
@ -29,50 +32,68 @@ TESTS = [
|
||||||
'run-tests.js'
|
'run-tests.js'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# This should be in the standard library somewhere, but as far as I
|
||||||
|
# can tell, isn't.
|
||||||
|
def import_file_as_module(path):
|
||||||
|
if 'test_www' not in sys.modules:
|
||||||
|
imp.load_source('test_www', www_path + '/__init__.py', StringIO())
|
||||||
|
|
||||||
|
tr = string.maketrans('-./%', '____')
|
||||||
|
modname = 'test_www.' + path.translate(tr)
|
||||||
|
try:
|
||||||
|
return sys.modules[modname]
|
||||||
|
except KeyError:
|
||||||
|
return imp.load_source(modname, path)
|
||||||
|
|
||||||
class FileHandler(SimpleHTTPServer.SimpleHTTPRequestHandler, object):
|
class FileHandler(SimpleHTTPServer.SimpleHTTPRequestHandler, object):
|
||||||
|
|
||||||
def do_GET(self):
|
def __init__(self, *args, **kwargs):
|
||||||
url = urlparse.urlparse(self.path)
|
self._cached_untranslated_path = None
|
||||||
if url.path == '/echo':
|
self._cached_translated_path = None
|
||||||
headers = {}
|
super(FileHandler, self).__init__(*args, **kwargs)
|
||||||
for name, value in self.headers.items():
|
|
||||||
headers[name] = value.rstrip()
|
|
||||||
d = dict(
|
|
||||||
command=self.command,
|
|
||||||
version=self.protocol_version,
|
|
||||||
origin=self.client_address,
|
|
||||||
url=self.path,
|
|
||||||
path=url.path,
|
|
||||||
params=url.params,
|
|
||||||
query=url.query,
|
|
||||||
fragment=url.fragment,
|
|
||||||
headers=headers
|
|
||||||
)
|
|
||||||
|
|
||||||
self.send_response(200)
|
|
||||||
self.send_header('Content-Type', 'application/json')
|
|
||||||
self.end_headers()
|
|
||||||
self.wfile.write(json.dumps(d, indent=2) + '\r\n')
|
|
||||||
return
|
|
||||||
|
|
||||||
if url.path == '/status':
|
|
||||||
self.send_response(int(url.query))
|
|
||||||
self.send_header('Content-Type', 'text/html')
|
|
||||||
self.end_headers()
|
|
||||||
self.wfile.write('Returning status ' + url.query + '\r\n')
|
|
||||||
return
|
|
||||||
|
|
||||||
super(FileHandler, self).do_GET()
|
|
||||||
|
|
||||||
# silent, do not pollute stdout nor stderr.
|
# silent, do not pollute stdout nor stderr.
|
||||||
def log_message(self, format, *args):
|
def log_message(self, format, *args):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# modified version allowing one to provide a .py file that will be
|
||||||
|
# interpreted to produce the response
|
||||||
|
def send_head(self):
|
||||||
|
path = self.translate_path(self.path)
|
||||||
|
py = path + '.py'
|
||||||
|
if not os.path.exists(path) and os.path.exists(py):
|
||||||
|
try:
|
||||||
|
mod = import_file_as_module(py)
|
||||||
|
return mod.handle_request(self)
|
||||||
|
except:
|
||||||
|
import cgitb
|
||||||
|
buf = StringIO.StringIO()
|
||||||
|
cgitb.Hook(file=buf).handle()
|
||||||
|
buf = buf.getvalue()
|
||||||
|
|
||||||
|
self.send_response(500, 'Internal Server Error')
|
||||||
|
self.send_header('Content-Type', 'text/html')
|
||||||
|
self.send_header('Content-Length', str(len(buf)))
|
||||||
|
self.end_headers()
|
||||||
|
return StringIO.StringIO(buf)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return super(FileHandler, self).send_head()
|
||||||
|
|
||||||
# modified version of SimpleHTTPRequestHandler's translate_path
|
# modified version of SimpleHTTPRequestHandler's translate_path
|
||||||
# to resolve the URL relative to the www/ directory
|
# to resolve the URL relative to the www/ directory
|
||||||
# (e.g. /foo -> test/www/foo)
|
# (e.g. /foo -> test/www/foo)
|
||||||
def translate_path(self, path):
|
def translate_path(self, path):
|
||||||
|
|
||||||
|
# Cache for efficiency, since our send_head calls this and
|
||||||
|
# then, in the normal case, the parent class's send_head
|
||||||
|
# immediately calls it again.
|
||||||
|
if (self._cached_translated_path is not None and
|
||||||
|
self._cached_untranslated_path == path):
|
||||||
|
return self._cached_translated_path
|
||||||
|
|
||||||
|
orig_path = path
|
||||||
|
|
||||||
# Strip query string and/or fragment, if present.
|
# Strip query string and/or fragment, if present.
|
||||||
x = path.find('?')
|
x = path.find('?')
|
||||||
if x != -1: path = path[:x]
|
if x != -1: path = path[:x]
|
||||||
|
@ -103,6 +124,9 @@ class FileHandler(SimpleHTTPServer.SimpleHTTPRequestHandler, object):
|
||||||
if trailing_slash:
|
if trailing_slash:
|
||||||
# it must be a '/' even on Windows
|
# it must be a '/' even on Windows
|
||||||
path += '/'
|
path += '/'
|
||||||
|
|
||||||
|
self._cached_untranslated_path = orig_path
|
||||||
|
self._cached_translated_path = path
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def run_httpd():
|
def run_httpd():
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import json
|
||||||
|
import urlparse
|
||||||
|
import cStringIO as StringIO
|
||||||
|
|
||||||
|
def handle_request(req):
|
||||||
|
url = urlparse.urlparse(req.path)
|
||||||
|
headers = {}
|
||||||
|
for name, value in req.headers.items():
|
||||||
|
headers[name] = value.rstrip()
|
||||||
|
|
||||||
|
d = dict(
|
||||||
|
command = req.command,
|
||||||
|
version = req.protocol_version,
|
||||||
|
origin = req.client_address,
|
||||||
|
url = req.path,
|
||||||
|
path = url.path,
|
||||||
|
params = url.params,
|
||||||
|
query = url.query,
|
||||||
|
fragment = url.fragment,
|
||||||
|
headers = headers
|
||||||
|
)
|
||||||
|
body = json.dumps(d, indent=2) + '\n'
|
||||||
|
|
||||||
|
req.send_response(200)
|
||||||
|
req.send_header('Content-Type', 'application/json')
|
||||||
|
req.send_header('Content-Length', str(len(body)))
|
||||||
|
req.end_headers()
|
||||||
|
return StringIO(body)
|
|
@ -0,0 +1,13 @@
|
||||||
|
import cStringIO as StringIO
|
||||||
|
import urlparse
|
||||||
|
|
||||||
|
def handle_request(req):
|
||||||
|
url = urlparse.urlparse(req.path)
|
||||||
|
|
||||||
|
body = "<!doctype html><h1>Status: {}</h1>".format(url.query)
|
||||||
|
|
||||||
|
self.send_response(int(url.query))
|
||||||
|
self.send_header('Content-Type', 'text/html')
|
||||||
|
self.send_header('Content-Length', str(len(body)))
|
||||||
|
self.end_headers()
|
||||||
|
return StringIO(body)
|
Loading…
Reference in New Issue