Skip to content
This repository has been archived by the owner on Jan 13, 2021. It is now read-only.

Commit

Permalink
Merge pull request #238 from Lukasa/upgrade
Browse files Browse the repository at this point in the history
Re-implement HTTP/2 plaintext upgrade, as before.
  • Loading branch information
Lukasa committed May 5, 2016
2 parents 23d1c29 + 5759d54 commit e9cc07f
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 6 deletions.
6 changes: 1 addition & 5 deletions hyper/common/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,9 @@ def get_response(self, *args, **kwargs):
self._host, self._port, **self._h2_kwargs
)

self._conn._sock = e.sock
self._conn._connect_upgrade(e.sock)
# stream id 1 is used by the upgrade request and response
# and is half-closed by the client
self._conn._new_stream(stream_id=1, local_closed=True)

# HTTP/2 preamble must be sent after receipt of a HTTP/1.1 101
self._conn._send_preamble()

return self._conn.get_response(1)

Expand Down
23 changes: 23 additions & 0 deletions hyper/http20/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,29 @@ def connect(self):

self._send_preamble()

def _connect_upgrade(self, sock):
"""
Called by the generic HTTP connection when we're being upgraded. Locks
in a new socket and places the backing state machine into an upgrade
state, then sends the preamble.
"""
self._sock = sock

with self._conn as conn:
conn.initiate_upgrade_connection()
conn.update_settings(
{h2.settings.ENABLE_PUSH: int(self._enable_push)}
)
self._send_outstanding_data()

# The server will also send an initial settings frame, so get it.
# However, we need to make sure our stream state is set up properly
# first, or any extra data we receive might cause us problems.
s = self._new_stream(local_closed=True)
self.recent_stream = s

self._recv_cb()

def _send_preamble(self):
"""
Sends the necessary HTTP/2 preamble.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def run_tests(self):


def resolve_install_requires():
basic_dependencies = ['h2~=2.0', 'hyperframe~=3.2']
basic_dependencies = ['h2>=2.3,<3.0', 'hyperframe~=3.2']

if py_version == (3, 3):
basic_dependencies.extend(
Expand Down
3 changes: 3 additions & 0 deletions test/test_abstraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ def __init__(self, host, port=None, secure=None, **kwargs):
def _send_preamble(self):
pass

def _connect_upgrade(self, sock):
self._sock = sock

def _new_stream(self, *args, **kwargs):
pass

Expand Down
76 changes: 76 additions & 0 deletions test/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,82 @@ def socket_handler(listener):

self.tear_down()

def test_upgrade(self):
self.set_up(secure=False)

recv_event = threading.Event()
wait_event = threading.Event()

def socket_handler(listener):
sock = listener.accept()[0]

# First read the HTTP/1.1 request
data = b''
while not data.endswith(b'\r\n\r\n'):
data += sock.recv(65535)

# Check it's an upgrade.
assert b'upgrade: h2c\r\n' in data

# Send back an upgrade message.
data = (
b'HTTP/1.1 101 Switching Protocols\r\n'
b'Server: some-server\r\n'
b'Connection: upgrade\r\n'
b'Upgrade: h2c\r\n'
b'\r\n'
)
sock.sendall(data)

# We get a message for connection open, specifically the preamble.
receive_preamble(sock)

# Now, send the headers for the response. This response has a body.
f = build_headers_frame([(':status', '200')])
f.stream_id = 1
sock.sendall(f.serialize())

# Send the first two chunks.
f = DataFrame(1)
f.data = b'hello'
sock.sendall(f.serialize())
f = DataFrame(1)
f.data = b'there'
sock.sendall(f.serialize())

# Now, delay a bit. We want to wait a half a second before we send
# the next frame.
wait_event.wait(5)
time.sleep(0.5)
f = DataFrame(1)
f.data = b'world'
f.flags.add('END_STREAM')
sock.sendall(f.serialize())

# Wait for the message from the main thread.
recv_event.set()
sock.close()

self._start_server(socket_handler)
conn = hyper.HTTPConnection(self.host, self.port, self.secure)
conn.request('GET', '/')
resp = conn.get_response()

# Confirm the status code.
assert resp.status == 200

first_chunk = resp.read(10)
wait_event.set()
second_chunk = resp.read(5)

assert first_chunk == b'hellothere'
assert second_chunk == b'world'

# Awesome, we're done now.
recv_event.wait(5)

self.tear_down()


class TestRequestsAdapter(SocketLevelTest):
# This uses HTTP/2.
Expand Down

0 comments on commit e9cc07f

Please sign in to comment.