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

Commit e9cc07f

Browse files
committed
Merge pull request #238 from Lukasa/upgrade
Re-implement HTTP/2 plaintext upgrade, as before.
2 parents 23d1c29 + 5759d54 commit e9cc07f

File tree

5 files changed

+104
-6
lines changed

5 files changed

+104
-6
lines changed

hyper/common/connection.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,9 @@ def get_response(self, *args, **kwargs):
137137
self._host, self._port, **self._h2_kwargs
138138
)
139139

140-
self._conn._sock = e.sock
140+
self._conn._connect_upgrade(e.sock)
141141
# stream id 1 is used by the upgrade request and response
142142
# and is half-closed by the client
143-
self._conn._new_stream(stream_id=1, local_closed=True)
144-
145-
# HTTP/2 preamble must be sent after receipt of a HTTP/1.1 101
146-
self._conn._send_preamble()
147143

148144
return self._conn.get_response(1)
149145

hyper/http20/connection.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,29 @@ def connect(self):
357357

358358
self._send_preamble()
359359

360+
def _connect_upgrade(self, sock):
361+
"""
362+
Called by the generic HTTP connection when we're being upgraded. Locks
363+
in a new socket and places the backing state machine into an upgrade
364+
state, then sends the preamble.
365+
"""
366+
self._sock = sock
367+
368+
with self._conn as conn:
369+
conn.initiate_upgrade_connection()
370+
conn.update_settings(
371+
{h2.settings.ENABLE_PUSH: int(self._enable_push)}
372+
)
373+
self._send_outstanding_data()
374+
375+
# The server will also send an initial settings frame, so get it.
376+
# However, we need to make sure our stream state is set up properly
377+
# first, or any extra data we receive might cause us problems.
378+
s = self._new_stream(local_closed=True)
379+
self.recent_stream = s
380+
381+
self._recv_cb()
382+
360383
def _send_preamble(self):
361384
"""
362385
Sends the necessary HTTP/2 preamble.

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def run_tests(self):
5353

5454

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

5858
if py_version == (3, 3):
5959
basic_dependencies.extend(

test/test_abstraction.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ def __init__(self, host, port=None, secure=None, **kwargs):
110110
def _send_preamble(self):
111111
pass
112112

113+
def _connect_upgrade(self, sock):
114+
self._sock = sock
115+
113116
def _new_stream(self, *args, **kwargs):
114117
pass
115118

test/test_integration.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,82 @@ def socket_handler(listener):
859859

860860
self.tear_down()
861861

862+
def test_upgrade(self):
863+
self.set_up(secure=False)
864+
865+
recv_event = threading.Event()
866+
wait_event = threading.Event()
867+
868+
def socket_handler(listener):
869+
sock = listener.accept()[0]
870+
871+
# First read the HTTP/1.1 request
872+
data = b''
873+
while not data.endswith(b'\r\n\r\n'):
874+
data += sock.recv(65535)
875+
876+
# Check it's an upgrade.
877+
assert b'upgrade: h2c\r\n' in data
878+
879+
# Send back an upgrade message.
880+
data = (
881+
b'HTTP/1.1 101 Switching Protocols\r\n'
882+
b'Server: some-server\r\n'
883+
b'Connection: upgrade\r\n'
884+
b'Upgrade: h2c\r\n'
885+
b'\r\n'
886+
)
887+
sock.sendall(data)
888+
889+
# We get a message for connection open, specifically the preamble.
890+
receive_preamble(sock)
891+
892+
# Now, send the headers for the response. This response has a body.
893+
f = build_headers_frame([(':status', '200')])
894+
f.stream_id = 1
895+
sock.sendall(f.serialize())
896+
897+
# Send the first two chunks.
898+
f = DataFrame(1)
899+
f.data = b'hello'
900+
sock.sendall(f.serialize())
901+
f = DataFrame(1)
902+
f.data = b'there'
903+
sock.sendall(f.serialize())
904+
905+
# Now, delay a bit. We want to wait a half a second before we send
906+
# the next frame.
907+
wait_event.wait(5)
908+
time.sleep(0.5)
909+
f = DataFrame(1)
910+
f.data = b'world'
911+
f.flags.add('END_STREAM')
912+
sock.sendall(f.serialize())
913+
914+
# Wait for the message from the main thread.
915+
recv_event.set()
916+
sock.close()
917+
918+
self._start_server(socket_handler)
919+
conn = hyper.HTTPConnection(self.host, self.port, self.secure)
920+
conn.request('GET', '/')
921+
resp = conn.get_response()
922+
923+
# Confirm the status code.
924+
assert resp.status == 200
925+
926+
first_chunk = resp.read(10)
927+
wait_event.set()
928+
second_chunk = resp.read(5)
929+
930+
assert first_chunk == b'hellothere'
931+
assert second_chunk == b'world'
932+
933+
# Awesome, we're done now.
934+
recv_event.wait(5)
935+
936+
self.tear_down()
937+
862938

863939
class TestRequestsAdapter(SocketLevelTest):
864940
# This uses HTTP/2.

0 commit comments

Comments
 (0)