Skip to content

Commit

Permalink
[WIP] Changed the way Python handlers work for H2, it now returns a F…
Browse files Browse the repository at this point in the history
…unctionHandler that has methods for specific frame handling. No longer requires a thread to parse the request. Refactored `finish_handling` to be cleaner split between h1 and h2.
  • Loading branch information
David Heiberg committed Aug 8, 2018
1 parent 01078f8 commit 59ee4f1
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 182 deletions.
2 changes: 2 additions & 0 deletions tools/wptserve/tests/functional/docroot/test_h2_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def handle_data(frame, request, response):
response.content = frame.data[::-1]
3 changes: 3 additions & 0 deletions tools/wptserve/tests/functional/docroot/test_h2_headers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def handle_headers(frame, request, response):
response.status = 203
response.headers.update([('test', 'passed')])
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def handle_headers(frame, request, response):
response.status = 203
response.headers.update([('test', 'passed')])

def handle_data(frame, request, response):
response.content = frame.data[::-1]
87 changes: 29 additions & 58 deletions tools/wptserve/tests/functional/test_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,82 +324,53 @@ def test_as_is(self):
#Add a check that the response is actually sane


class TestH2ResponseHandler(TestUsingH2Server):
class TestH2Handler(TestUsingH2Server):

def test_handle_headers(self):

class Handler(wptserve.handlers.H2ResponseHandler):
def handle_headers(self, frame, request, response):
if request.headers['path'] == "/h2test/test_handler":
response.headers.update([('test', 'passed')])
response.status = 203
else:
response.headers.update([('test', 'failed')])

route = ("GET", "/h2test/test_handler", Handler())
self.server.router.register(*route)
self.conn.request(route[0], route[1])
self.conn.request("GET", '/test_h2_headers.py')
resp = self.conn.get_response()

assert resp.status == 203
assert resp.headers['test'][0] == 'passed'
assert resp.read() == ''

def test_handle_data(self):

class Handler(wptserve.handlers.H2ResponseHandler):
def handle_data(self, frame, request, response):
data = frame.data
response.content = ''.join(reversed(data))
def test_only_main(self):
self.conn.request("GET", '/test_tuple_3.py')
resp = self.conn.get_response()

route = ("POST", "/h2test/test_handler", Handler())
self.server.router.register(*route)
self.conn.request(route[0], route[1], body="hello world")
assert resp.status == 202
assert resp.headers['Content-Type'][0] == 'text/html'
assert resp.headers['X-Test'][0] == 'PASS'
assert resp.read() == b'PASS'

def test_handle_data(self):
self.conn.request("POST", '/test_h2_data.py', body="hello world!")
resp = self.conn.get_response()
assert resp.status == 200
assert resp.read() == 'dlrow olleh'

def test_handle_data_no_headers(self):
# The HTTP/2.0 protocol does allow for just DATA frames to be written when a stream is
# in the OPEN state.
class Handler(wptserve.handlers.H2ResponseHandler):
def handle_data(self, frame, request, response):
data = frame.data
response.content = ''.join(reversed(data))
response.write_content()

route = ("POST", "/h2test/test_handler", Handler())
self.server.router.register(*route)
sid = self.conn.request(route[0], route[1], body="hello world")
assert self.conn.streams[sid]._read() == 'dlrow olleh'

assert resp.status == 200
assert resp.read() == b'!dlrow olleh'

def test_handle_headers_data(self):

class Handler(wptserve.handlers.H2ResponseHandler):
def handle_headers(self, frame, request, response):
if request.headers['path'] == "/h2test/test_handler":
response.headers.update([('test', 'passed')])
response.status = 203
else:
response.headers.update([('test', 'failed')])
response.status = 404
response.write_status_headers()

def handle_data(self, frame, request, response):
data = frame.data
response.content = ''.join(reversed(data))
response.write_content()

route = ("POST", "/h2test/test_handler", Handler())
self.server.router.register(*route)
self.conn.request(route[0], route[1], body="hello world")
self.conn.request("POST", '/test_h2_headers_data.py', body="hello world!")
resp = self.conn.get_response()

assert resp.status == 203
assert resp.headers['test'][0] == 'passed'
assert resp.read() == 'dlrow olleh'
assert resp.read() == b'!dlrow olleh'

def test_no_main_or_handlers(self):
self.conn.request("GET", '/no_main.py')
resp = self.conn.get_response()

assert resp.status == 500
assert "No main function or handlers in script " in json.loads(resp.read())["error"]["message"]

def test_not_found(self):
self.conn.request("GET", '/no_exist.py')
resp = self.conn.get_response()

assert resp.status == 404


if __name__ == '__main__':
unittest.main()
71 changes: 33 additions & 38 deletions tools/wptserve/wptserve/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ def __init__(self, base_path=None, url_base="/"):
def __repr__(self):
return "<%s base_path:%s url_base:%s>" % (self.__class__.__name__, self.base_path, self.url_base)

def __call__(self, request, response):
def read_file_and_run(self, request, response, func):
path = filesystem_path(self.base_path, request, self.url_base)

sys_path = sys.path[:]
Expand All @@ -242,22 +242,45 @@ def __call__(self, request, response):
sys.path.insert(0, os.path.dirname(path))
with open(path, 'rb') as f:
exec(compile(f.read(), path, 'exec'), environ, environ)

return func(request, response, environ, path)

except IOError:
raise HTTPException(404)
finally:
sys.path = sys_path
sys.modules = sys_modules

def __call__(self, request, response):
def func(request, response, environ, path):
if "main" in environ:
handler = FunctionHandler(environ["main"])
handler(request, response)
wrap_pipeline(path, request, response)
elif "h2main" in environ:
handler = environ["h2main"](H2ResponseHandler)
handler(request, response)
wrap_pipeline(path, request, response)
else:
raise HTTPException(500, "No main function in script %s" % path)
except IOError:
raise HTTPException(404)
finally:
sys.path = sys_path
sys.modules = sys_modules

self.read_file_and_run(request, response, func)


def generate_handler(self, request):
def func(request, response, environ, path):
def _main(req, resp):
pass

handler = FunctionHandler(_main)
if "main" in environ:
handler.func = environ["main"]
if "handle_headers" in environ:
handler.handle_headers = environ["handle_headers"]
if "handle_data" in environ:
handler.handle_data = environ["handle_data"]

if handler.func is _main and not hasattr(handler, "handle_headers") and not hasattr(handler, "handle_data"):
raise HTTPException(500, "No main function or handlers in script %s" % path)

return handler
return self.read_file_and_run(request, None, func)

python_script_handler = PythonScriptHandler()

Expand Down Expand Up @@ -293,34 +316,6 @@ def __call__(self, request, response):
def handler(func):
return FunctionHandler(func)


class H2ResponseHandler(object):

def __init__(self):
self.func_map = {
'headers': None,
'data': None,
}

def __call__(self, request, response):
while True:
frame = request.frames.get(True, None)
if isinstance(frame, RequestReceived):
self.handle_headers(frame, request, response)
elif isinstance(frame, DataReceived):
self.handle_data(frame, request, response)
else:
raise ValueError('Frame type not recognized: ' + str(frame))

if frame.stream_ended:
break

def handle_headers(self, frame, request, response):
pass

def handle_data(self, frame, request, response):
pass

class JsonHandler(object):
def __init__(self, func):
self.func = func
Expand Down
3 changes: 1 addition & 2 deletions tools/wptserve/wptserve/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import tempfile

from six.moves.urllib.parse import parse_qsl, urlsplit
from six.moves.queue import Queue

from . import stash
from .utils import HTTPException
Expand Down Expand Up @@ -352,7 +351,7 @@ def auth(self):
class H2Request(Request):
def __init__(self, request_handler):
self.h2_stream_id = request_handler.h2_stream_id
self.frames = Queue()
self.frames = []
super(H2Request, self).__init__(request_handler)


Expand Down
Loading

0 comments on commit 59ee4f1

Please sign in to comment.