Created
April 14, 2015 12:27
-
-
Save Lukasa/a8b392f3f41433a9a473 to your computer and use it in GitHub Desktop.
Twisted HTTP/2 spike
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Twisted==15.1.0 | |
hpack==1.0.0 | |
hyperframe==1.0.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
""" | |
A brief spike to test a Twisted implementation of HTTP/2. | |
""" | |
import json | |
import time | |
from twisted.internet import protocol, reactor | |
from hyperframe import frame as h2 | |
from hpack.hpack import Encoder, Decoder | |
class HTTP2Protocol(protocol.Protocol): | |
""" | |
A Twisted HTTP/2 server protocol (maybe). | |
""" | |
def __init__(self, *args, **kwargs): | |
self.buffer = b'' | |
self.streams = {} | |
self.encoder = Encoder() | |
self.decoder = Decoder() | |
def dataReceived(self, data): | |
""" | |
HTTP/2 data has arrived. | |
""" | |
# COPY ALL THE DATA | |
self.buffer += data | |
buffer = memoryview(self.buffer) | |
while True: | |
if len(buffer) < 9: | |
break | |
f, length = h2.Frame.parse_frame_header(buffer[:9]) | |
if len(buffer) < (length + 9): | |
break | |
f.parse_body(buffer[9:9+length]) | |
print f | |
buffer = buffer[9+length:] | |
self.handle_frame(f) | |
self.buffer = buffer.tobytes() | |
def handle_frame(self, frame): | |
if isinstance(frame, h2.HeadersFrame): | |
# A new stream! | |
self.streams[frame.stream_id] = RequestHandler(self) | |
headers = self.decoder.decode(frame.data) | |
self.streams[frame.stream_id].requestBegin( | |
frame.stream_id, | |
headers, | |
) | |
if 'END_STREAM' in frame.flags: | |
self.streams[frame.stream_id].requestComplete() | |
elif isinstance(frame, h2.DataFrame): | |
self.streams[frame.stream_id].dataReceived(frame.data) | |
if 'END_STREAM' in frame.flags: | |
self.streams[frame.stream_id].requestComplete() | |
elif isinstance(frame, h2.WindowUpdateFrame): | |
# TODO: maybe some flow control maybe? | |
pass | |
elif isinstance(frame, h2.GoAwayFrame): | |
# TODO: handle connection teardown | |
pass | |
elif isinstance(frame, h2.PingFrame): | |
# Send it back. | |
frame.flags.add('ACK') | |
self.transport.write(frame.serialize()) | |
elif isinstance(frame, h2.SettingsFrame): | |
# Echo back the frame. | |
frame.flags.add('ACK') | |
self.transport.write(frame.serialize()) | |
elif isinstance(frame, h2.RstStreamFrame): | |
# TODO: handle stream teardown. | |
pass | |
def beginResponse(self, stream_id, headers): | |
f = h2.HeadersFrame(stream_id) | |
f.data = self.encoder.encode(headers) | |
f.flags = set(['END_HEADERS']) | |
self.transport.write(f.serialize()) | |
def sendData(self, stream_id, data): | |
f = h2.DataFrame(stream_id) | |
f.data = data | |
self.transport.write(f.serialize()) | |
def endResponse(self, stream_id, data=b''): | |
f = h2.DataFrame(stream_id) | |
f.data = data | |
f.flags = set(['END_STREAM']) | |
self.transport.write(f.serialize()) | |
del self.streams[stream_id] | |
class RequestHandler(object): | |
""" | |
A simple object that returns basic 200 responses to requests. | |
""" | |
def __init__(self, protocol): | |
self.protocol = protocol | |
def requestBegin(self, stream_id, headers): | |
self.stream_id = stream_id | |
self.headers = headers | |
self.data = [] | |
print headers | |
def requestComplete(self): | |
response_headers = [ | |
(':status', '200'), | |
('server', 'hyper-twisted/0.0.1 twisted/15.1.0'), | |
('content-type', 'application/json'), | |
] | |
self.protocol.beginResponse(self.stream_id, response_headers) | |
response = { | |
'headers': self.headers, | |
'data': b''.join(self.data), | |
'time': time.time(), | |
} | |
self.protocol.sendData(self.stream_id, json.dumps(response)) | |
self.protocol.endResponse(self.stream_id) | |
def dataReceived(self, data): | |
self.data.append(data) | |
print data | |
factory = protocol.Factory() | |
factory.protocol = HTTP2Protocol | |
reactor.listenTCP(8888, factory) | |
reactor.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment