From bb868fd90973cf8128100b1f61cb02c7c37dd511 Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Tue, 21 Feb 2017 16:16:26 +0900 Subject: [PATCH 01/11] Add ENABLE_PUSH flag in the Upgrade HTTP2-Settings header Push flag required for the case the initial upgrade request triggered server push. --- hyper/common/connection.py | 3 ++- hyper/http11/connection.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hyper/common/connection.py b/hyper/common/connection.py index dee18d68..507a8ad7 100644 --- a/hyper/common/connection.py +++ b/hyper/common/connection.py @@ -62,7 +62,8 @@ def __init__(self, self._port = port self._h1_kwargs = { 'secure': secure, 'ssl_context': ssl_context, - 'proxy_host': proxy_host, 'proxy_port': proxy_port + 'proxy_host': proxy_host, 'proxy_port': proxy_port, + 'enable_push': enable_push } self._h2_kwargs = { 'window_manager': window_manager, 'enable_push': enable_push, diff --git a/hyper/http11/connection.py b/hyper/http11/connection.py index 48bde2f0..027b2648 100644 --- a/hyper/http11/connection.py +++ b/hyper/http11/connection.py @@ -78,6 +78,7 @@ def __init__(self, host, port=None, secure=None, ssl_context=None, # only send http upgrade headers for non-secure connection self._send_http_upgrade = not self.secure + self._enable_push = int(kwargs.get('enable_push', False)) self.ssl_context = ssl_context self._sock = None @@ -276,6 +277,7 @@ def _add_upgrade_headers(self, headers): # Settings header. http2_settings = SettingsFrame(0) http2_settings.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = 65535 + http2_settings.settings[SettingsFrame.ENABLE_PUSH] = self._enable_push encoded_settings = base64.urlsafe_b64encode( http2_settings.serialize_body() ) From ec0c0dcc16b4a6a8a05e95303c596dbda60d5897 Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Tue, 21 Feb 2017 09:16:58 +0000 Subject: [PATCH 02/11] make the http11 connection enable_push optional If enable_push option was not explicitly set, HTTP2-Settings header will not include ENABLE_PUSH option. --- hyper/http11/connection.py | 5 +++-- test/test_abstraction.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hyper/http11/connection.py b/hyper/http11/connection.py index 027b2648..519db624 100644 --- a/hyper/http11/connection.py +++ b/hyper/http11/connection.py @@ -78,7 +78,7 @@ def __init__(self, host, port=None, secure=None, ssl_context=None, # only send http upgrade headers for non-secure connection self._send_http_upgrade = not self.secure - self._enable_push = int(kwargs.get('enable_push', False)) + self._enable_push = kwargs.get('enable_push') self.ssl_context = ssl_context self._sock = None @@ -277,7 +277,8 @@ def _add_upgrade_headers(self, headers): # Settings header. http2_settings = SettingsFrame(0) http2_settings.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = 65535 - http2_settings.settings[SettingsFrame.ENABLE_PUSH] = self._enable_push + if self._enable_push is not None: + http2_settings.settings[SettingsFrame.ENABLE_PUSH] = int(self._enable_push) encoded_settings = base64.urlsafe_b64encode( http2_settings.serialize_body() ) diff --git a/test/test_abstraction.py b/test/test_abstraction.py index cd0e0645..7c2cad1a 100644 --- a/test/test_abstraction.py +++ b/test/test_abstraction.py @@ -19,6 +19,7 @@ def test_h1_kwargs(self): 'proxy_host': False, 'proxy_port': False, 'other_kwarg': True, + 'enable_push': True, } def test_h2_kwargs(self): From 8f33457843e2faf0e7bae933ff0553a5fcc07c6a Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Tue, 21 Feb 2017 09:31:53 +0000 Subject: [PATCH 03/11] apply autopep8 fix for `flake8 --max-complexity 15` --- hyper/http11/connection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hyper/http11/connection.py b/hyper/http11/connection.py index 519db624..5febdc61 100644 --- a/hyper/http11/connection.py +++ b/hyper/http11/connection.py @@ -278,7 +278,8 @@ def _add_upgrade_headers(self, headers): http2_settings = SettingsFrame(0) http2_settings.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = 65535 if self._enable_push is not None: - http2_settings.settings[SettingsFrame.ENABLE_PUSH] = int(self._enable_push) + http2_settings.settings[ + SettingsFrame.ENABLE_PUSH] = int(self._enable_push) encoded_settings = base64.urlsafe_b64encode( http2_settings.serialize_body() ) From ddbaefd930dc4977865b70c45815fd6bbd206fe2 Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Tue, 21 Feb 2017 10:27:15 +0000 Subject: [PATCH 04/11] change code formatting --- hyper/http11/connection.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hyper/http11/connection.py b/hyper/http11/connection.py index 5febdc61..568d0aa3 100644 --- a/hyper/http11/connection.py +++ b/hyper/http11/connection.py @@ -278,8 +278,9 @@ def _add_upgrade_headers(self, headers): http2_settings = SettingsFrame(0) http2_settings.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = 65535 if self._enable_push is not None: - http2_settings.settings[ - SettingsFrame.ENABLE_PUSH] = int(self._enable_push) + http2_settings.settings[SettingsFrame.ENABLE_PUSH] = ( + int(self._enable_push) + ) encoded_settings = base64.urlsafe_b64encode( http2_settings.serialize_body() ) @@ -352,7 +353,7 @@ def _send_file_like_obj(self, fobj): Handles streaming a file-like object to the network. """ while True: - block = fobj.read(16*1024) + block = fobj.read(16 * 1024) if not block: break From 36270936324e135bbf237a3a400fa5e5f548bcf9 Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Tue, 21 Feb 2017 23:21:15 +0000 Subject: [PATCH 05/11] add server push test with h2c upgrade --- hyper/common/connection.py | 16 ++++++++++++++++ hyper/http11/connection.py | 19 ++++++++++++++++++- hyper/http20/connection.py | 11 +++++++++-- test/test_hyper.py | 31 ++++++++++++++++++++++++++----- 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/hyper/common/connection.py b/hyper/common/connection.py index 507a8ad7..c2deb757 100644 --- a/hyper/common/connection.py +++ b/hyper/common/connection.py @@ -144,6 +144,22 @@ def get_response(self, *args, **kwargs): return self._conn.get_response(1) + def get_pushes(self, *args, **kwargs): + try: + return self._conn.get_pushes(*args, **kwargs) + except HTTPUpgrade as e: + assert e.negotiated == H2C_PROTOCOL + + self._conn = HTTP20Connection( + self._host, self._port, **self._h2_kwargs + ) + + self._conn._connect_upgrade(e.sock, True) + # stream id 1 is used by the upgrade request and response + # and is half-closed by the client + + return self._conn.get_pushes(*args, **kwargs) + # The following two methods are the implementation of the context manager # protocol. def __enter__(self): # pragma: no cover diff --git a/hyper/http11/connection.py b/hyper/http11/connection.py index 568d0aa3..d2722993 100644 --- a/hyper/http11/connection.py +++ b/hyper/http11/connection.py @@ -58,6 +58,7 @@ class HTTP11Connection(object): """ version = HTTPVersion.http11 + _response = None def __init__(self, host, port=None, secure=None, ssl_context=None, proxy_host=None, proxy_port=None, **kwargs): @@ -105,6 +106,12 @@ def __init__(self, host, port=None, secure=None, ssl_context=None, #: the standard hyper parsing interface. self.parser = Parser() + def get_pushes(self, stream_id=None, capture_all=False): + """ + Dummy method to trigger h2c upgrade. + """ + self._get_response() + def connect(self): """ Connect to the server specified when the object was created. This is a @@ -189,6 +196,7 @@ def request(self, method, url, body=None, headers=None): # Next, send the request body. if body: self._send_body(body, body_type) + self._response = None return @@ -199,6 +207,14 @@ def get_response(self): This is an early beta, so the response object is pretty stupid. That's ok, we'll fix it later. """ + resp = self._get_response() + self._response = None + return resp + + def _get_response(self): + if self._response is not None: + return self._response + headers = HTTPHeaderMap() response = None @@ -217,13 +233,14 @@ def get_response(self): H2C_PROTOCOL.encode('utf-8') in headers['upgrade']): raise HTTPUpgrade(H2C_PROTOCOL, self._sock) - return HTTP11Response( + self._response = HTTP11Response( response.status, response.msg.tobytes(), headers, self._sock, self ) + return self._response def _send_headers(self, method, url, headers): """ diff --git a/hyper/http20/connection.py b/hyper/http20/connection.py index 8b2a71e8..f6c44250 100644 --- a/hyper/http20/connection.py +++ b/hyper/http20/connection.py @@ -114,6 +114,7 @@ def __init__(self, host, port=None, secure=None, window_manager=None, else: self.secure = False + self._delay_recv = False self._enable_push = enable_push self.ssl_context = ssl_context @@ -313,6 +314,9 @@ def get_response(self, stream_id=None): get a response. :returns: A :class:`HTTP20Response ` object. """ + if self._delay_recv: + self._recv_cb() + self._delay_recv = False stream = self._get_stream(stream_id) return HTTP20Response(stream.getheaders(), stream) @@ -384,7 +388,7 @@ def connect(self): self._send_preamble() - def _connect_upgrade(self, sock): + def _connect_upgrade(self, sock, no_recv=False): """ 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 @@ -405,7 +409,10 @@ def _connect_upgrade(self, sock): s = self._new_stream(local_closed=True) self.recent_stream = s - self._recv_cb() + if no_recv: # To delay I/O operation + self._recv_delay = True + else: + self._recv_cb() def _send_preamble(self): """ diff --git a/test/test_hyper.py b/test/test_hyper.py index 6a18d592..892c572b 100644 --- a/test/test_hyper.py +++ b/test/test_hyper.py @@ -9,6 +9,7 @@ PingFrame, FRAME_MAX_ALLOWED_LEN ) from hpack.hpack_compat import Encoder +from hyper import HTTPConnection from hyper.http20.connection import HTTP20Connection from hyper.http20.response import HTTP20Response, HTTP20Push from hyper.http20.exceptions import ConnectionError, StreamResetError @@ -731,8 +732,8 @@ def add_data_frame(self, stream_id, data, end_stream=False): frame.flags.add('END_STREAM') self.frames.append(frame) - def request(self): - self.conn = HTTP20Connection('www.google.com', enable_push=True) + def request(self, enable_push=True): + self.conn = HTTP20Connection('www.google.com', enable_push=enable_push) self.conn._sock = DummySocket() self.conn._sock.buffer = BytesIO( b''.join([frame.serialize() for frame in self.frames]) @@ -934,13 +935,13 @@ def test_reset_pushed_streams_when_push_disabled(self): 1, [(':status', '200'), ('content-type', 'text/html')] ) - self.request() - self.conn._enable_push = False + self.request(enable_push=False) self.conn.get_response() f = RstStreamFrame(2) f.error_code = 7 - assert self.conn._sock.queue[-1] == f.serialize() + print(self.conn._sock.queue) + assert self.conn._sock.queue[-1].endswith(f.serialize()) def test_pushed_requests_ignore_unexpected_headers(self): headers = HTTPHeaderMap([ @@ -956,6 +957,26 @@ def test_pushed_requests_ignore_unexpected_headers(self): assert p.request_headers == HTTPHeaderMap([('no', 'no')]) +class TestUpgradingPush(TestServerPush): + http101 = (b"HTTP/1.1 101 Switching Protocols\r\n" + b"Connection: upgrade\r\n" + b"Upgrade: h2c\r\n" + b"\r\n") + + def setup_method(self, method): + self.frames = [SettingsFrame(0)] # Server-side preface + self.encoder = Encoder() + self.conn = None + + def request(self, enable_push=True): + self.conn = HTTPConnection('www.google.com', enable_push=enable_push) + self.conn._conn._sock = DummySocket() + self.conn._conn._sock.buffer = BytesIO( + self.http101 + b''.join([frame.serialize() for frame in self.frames]) + ) + self.conn.request('GET', '/') + + class TestResponse(object): def test_status_is_stripped_from_headers(self): headers = HTTPHeaderMap([(':status', '200')]) From b770b67606d285dfff7854b2a235c413bee051bb Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Tue, 21 Feb 2017 23:31:16 +0000 Subject: [PATCH 06/11] apply autopep8 --- test/test_hyper.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_hyper.py b/test/test_hyper.py index 892c572b..52ce54bc 100644 --- a/test/test_hyper.py +++ b/test/test_hyper.py @@ -959,12 +959,12 @@ def test_pushed_requests_ignore_unexpected_headers(self): class TestUpgradingPush(TestServerPush): http101 = (b"HTTP/1.1 101 Switching Protocols\r\n" - b"Connection: upgrade\r\n" - b"Upgrade: h2c\r\n" - b"\r\n") + b"Connection: upgrade\r\n" + b"Upgrade: h2c\r\n" + b"\r\n") def setup_method(self, method): - self.frames = [SettingsFrame(0)] # Server-side preface + self.frames = [SettingsFrame(0)] # Server-side preface self.encoder = Encoder() self.conn = None From d644eabd360ae260f286eb9950d6d82acc5f7ad7 Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Tue, 21 Feb 2017 23:36:56 +0000 Subject: [PATCH 07/11] apply autopep8 --- test/test_hyper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_hyper.py b/test/test_hyper.py index 52ce54bc..a1faf89f 100644 --- a/test/test_hyper.py +++ b/test/test_hyper.py @@ -972,12 +972,14 @@ def request(self, enable_push=True): self.conn = HTTPConnection('www.google.com', enable_push=enable_push) self.conn._conn._sock = DummySocket() self.conn._conn._sock.buffer = BytesIO( - self.http101 + b''.join([frame.serialize() for frame in self.frames]) + self.http101 + b''.join([frame.serialize() + for frame in self.frames]) ) self.conn.request('GET', '/') class TestResponse(object): + def test_status_is_stripped_from_headers(self): headers = HTTPHeaderMap([(':status', '200')]) resp = HTTP20Response(headers, None) From ffe0ea1a113b40ada941af131e4bb7530e77809e Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Tue, 21 Feb 2017 23:46:43 +0000 Subject: [PATCH 08/11] apply autopep8 --- hyper/http20/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyper/http20/connection.py b/hyper/http20/connection.py index f6c44250..994f6ed6 100644 --- a/hyper/http20/connection.py +++ b/hyper/http20/connection.py @@ -409,7 +409,7 @@ def _connect_upgrade(self, sock, no_recv=False): s = self._new_stream(local_closed=True) self.recent_stream = s - if no_recv: # To delay I/O operation + if no_recv: # To delay I/O operation self._recv_delay = True else: self._recv_cb() From 7d172cae23d81f6f33f1d4dfefa1f2d2c4b12bd8 Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Tue, 21 Feb 2017 23:59:40 +0000 Subject: [PATCH 09/11] fix typo --- hyper/http11/connection.py | 43 +++++++++++++++++++------------------- hyper/http20/connection.py | 2 +- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/hyper/http11/connection.py b/hyper/http11/connection.py index d2722993..52377401 100644 --- a/hyper/http11/connection.py +++ b/hyper/http11/connection.py @@ -212,34 +212,33 @@ def get_response(self): return resp def _get_response(self): - if self._response is not None: - return self._response + if self._response is None: - headers = HTTPHeaderMap() + headers = HTTPHeaderMap() - response = None - while response is None: - # 'encourage' the socket to receive data. - self._sock.fill() - response = self.parser.parse_response(self._sock.buffer) + response = None + while response is None: + # 'encourage' the socket to receive data. + self._sock.fill() + response = self.parser.parse_response(self._sock.buffer) - for n, v in response.headers: - headers[n.tobytes()] = v.tobytes() + for n, v in response.headers: + headers[n.tobytes()] = v.tobytes() - self._sock.advance_buffer(response.consumed) + self._sock.advance_buffer(response.consumed) - if (response.status == 101 and + if (response.status == 101 and b'upgrade' in headers['connection'] and - H2C_PROTOCOL.encode('utf-8') in headers['upgrade']): - raise HTTPUpgrade(H2C_PROTOCOL, self._sock) - - self._response = HTTP11Response( - response.status, - response.msg.tobytes(), - headers, - self._sock, - self - ) + H2C_PROTOCOL.encode('utf-8') in headers['upgrade']): + raise HTTPUpgrade(H2C_PROTOCOL, self._sock) + + self._response = HTTP11Response( + response.status, + response.msg.tobytes(), + headers, + self._sock, + self + ) return self._response def _send_headers(self, method, url, headers): diff --git a/hyper/http20/connection.py b/hyper/http20/connection.py index 994f6ed6..7223833d 100644 --- a/hyper/http20/connection.py +++ b/hyper/http20/connection.py @@ -410,7 +410,7 @@ def _connect_upgrade(self, sock, no_recv=False): self.recent_stream = s if no_recv: # To delay I/O operation - self._recv_delay = True + self._delay_recv = True else: self._recv_cb() From 7a4c8b69e50426d7177ee0290c5ca587dedd4399 Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Thu, 23 Feb 2017 06:39:40 +0000 Subject: [PATCH 10/11] fix test synchronization Some tests requires synchronization to let the connection state machine working. --- test/test_integration.py | 119 +++++++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 36 deletions(-) diff --git a/test/test_integration.py b/test/test_integration.py index 96a8af76..4bffa641 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -12,6 +12,7 @@ import hyper import hyper.http11.connection import pytest +from contextlib import contextmanager from mock import patch from h2.frame_buffer import FrameBuffer from hyper.compat import ssl @@ -64,17 +65,30 @@ def frame_buffer(): return buffer +@contextmanager +def reusable_frame_buffer(buffer): + # FrameBuffer does not return new iterator for iteration. + data = buffer.data + yield buffer + buffer.data = data + + def receive_preamble(sock): # Receive the HTTP/2 'preamble'. - first = sock.recv(65535) + client_preface = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n' + timeout = time.time() + 5 + got = b'' + while len(got) < len(client_preface) and time.time() < timeout: + got += sock.recv(len(client_preface) - len(got)) + + assert got == client_preface, "client preface mismatch" - # Work around some bugs: if the first message received was only the PRI - # string, aim to receive a settings frame as well. - if len(first) <= len(b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'): - sock.recv(65535) + # Send server side HTTP/2 preface sock.send(SettingsFrame(0).serialize()) - sock.recv(65535) - return + # Drain to let the client proceed. + # Note that in the lower socket level, this method is not + # just doing "receive". + return sock.recv(65535) @patch('hyper.http20.connection.H2_NPN_PROTOCOLS', PROTOCOLS) @@ -138,7 +152,7 @@ def socket_handler(listener): self._start_server(socket_handler) conn = self.get_connection() conn.connect() - send_event.wait() + send_event.wait(5) # Get the chunk of data after the preamble and decode it into frames. # We actually expect two, but only the second one contains ENABLE_PUSH. @@ -242,7 +256,7 @@ def socket_handler(listener): f = SettingsFrame(0) sock.send(f.serialize()) - send_event.wait() + send_event.wait(5) sock.recv(65535) sock.close() @@ -260,6 +274,7 @@ def socket_handler(listener): def test_closed_responses_remove_their_streams_from_conn(self): self.set_up() + req_event = threading.Event() recv_event = threading.Event() def socket_handler(listener): @@ -270,6 +285,8 @@ def socket_handler(listener): receive_preamble(sock) sock.recv(65535) + # Wait for request + req_event.wait(5) # Now, send the headers for the response. f = build_headers_frame([(':status', '200')]) f.stream_id = 1 @@ -282,6 +299,7 @@ def socket_handler(listener): self._start_server(socket_handler) conn = self.get_connection() conn.request('GET', '/') + req_event.set() resp = conn.get_response() # Close the response. @@ -296,6 +314,7 @@ def socket_handler(listener): def test_receiving_responses_with_no_body(self): self.set_up() + req_event = threading.Event() recv_event = threading.Event() def socket_handler(listener): @@ -306,6 +325,8 @@ def socket_handler(listener): receive_preamble(sock) sock.recv(65535) + # Wait for request + req_event.wait(5) # Now, send the headers for the response. This response has no body f = build_headers_frame( [(':status', '204'), ('content-length', '0')] @@ -321,6 +342,7 @@ def socket_handler(listener): self._start_server(socket_handler) conn = self.get_connection() conn.request('GET', '/') + req_event.set() resp = conn.get_response() # Confirm the status code. @@ -338,6 +360,7 @@ def socket_handler(listener): def test_receiving_trailers(self): self.set_up() + req_event = threading.Event() recv_event = threading.Event() def socket_handler(listener): @@ -350,6 +373,8 @@ def socket_handler(listener): receive_preamble(sock) sock.recv(65535) + # Wait for request + req_event.wait(5) # Now, send the headers for the response. f = build_headers_frame( [(':status', '200'), ('content-length', '14')], @@ -372,12 +397,13 @@ def socket_handler(listener): sock.send(f.serialize()) # Wait for the message from the main thread. - recv_event.set() + recv_event.wait(5) sock.close() self._start_server(socket_handler) conn = self.get_connection() conn.request('GET', '/') + req_event.set() resp = conn.get_response() # Confirm the status code. @@ -396,13 +422,14 @@ def socket_handler(listener): assert len(resp.trailers) == 2 # Awesome, we're done now. - recv_event.wait(5) + recv_event.set() self.tear_down() def test_receiving_trailers_before_reading(self): self.set_up() + req_event = threading.Event() recv_event = threading.Event() wait_event = threading.Event() @@ -416,6 +443,8 @@ def socket_handler(listener): receive_preamble(sock) sock.recv(65535) + # Wait for request + req_event.wait(5) # Now, send the headers for the response. f = build_headers_frame( [(':status', '200'), ('content-length', '14')], @@ -449,6 +478,7 @@ def socket_handler(listener): self._start_server(socket_handler) conn = self.get_connection() conn.request('GET', '/') + req_event.set() resp = conn.get_response() # Confirm the status code. @@ -647,6 +677,7 @@ def test_resetting_stream_with_frames_in_flight(self): """ self.set_up() + req_event = threading.Event() recv_event = threading.Event() def socket_handler(listener): @@ -657,6 +688,8 @@ def socket_handler(listener): receive_preamble(sock) sock.recv(65535) + # Wait for request + req_event.wait(5) # Now, send the headers for the response. This response has no # body. f = build_headers_frame( @@ -673,6 +706,7 @@ def socket_handler(listener): self._start_server(socket_handler) conn = self.get_connection() stream_id = conn.request('GET', '/') + req_event.set() # Now, trigger the RST_STREAM frame by closing the stream. conn._send_rst_frame(stream_id, 0) @@ -696,6 +730,7 @@ def test_stream_can_be_reset_multiple_times(self): """ self.set_up() + req_event = threading.Event() recv_event = threading.Event() def socket_handler(listener): @@ -706,6 +741,8 @@ def socket_handler(listener): receive_preamble(sock) sock.recv(65535) + # Wait for request + req_event.wait(5) # Now, send two RST_STREAM frames. for _ in range(0, 2): f = RstStreamFrame(1) @@ -718,6 +755,7 @@ def socket_handler(listener): self._start_server(socket_handler) conn = self.get_connection() conn.request('GET', '/') + req_event.set() # Now, eat the Rst frames. These should not cause an exception. conn._single_read() @@ -737,6 +775,7 @@ def socket_handler(listener): def test_read_chunked_http2(self): self.set_up() + req_event = threading.Event() recv_event = threading.Event() wait_event = threading.Event() @@ -748,6 +787,8 @@ def socket_handler(listener): receive_preamble(sock) sock.recv(65535) + # Wait for request + req_event.wait(5) # Now, send the headers for the response. This response has a body. f = build_headers_frame([(':status', '200')]) f.stream_id = 1 @@ -777,6 +818,7 @@ def socket_handler(listener): self._start_server(socket_handler) conn = self.get_connection() conn.request('GET', '/') + req_event.set() resp = conn.get_response() # Confirm the status code. @@ -805,6 +847,7 @@ def socket_handler(listener): def test_read_delayed(self): self.set_up() + req_event = threading.Event() recv_event = threading.Event() wait_event = threading.Event() @@ -816,6 +859,8 @@ def socket_handler(listener): receive_preamble(sock) sock.recv(65535) + # Wait for request + req_event.wait(5) # Now, send the headers for the response. This response has a body. f = build_headers_frame([(':status', '200')]) f.stream_id = 1 @@ -845,6 +890,7 @@ def socket_handler(listener): self._start_server(socket_handler) conn = self.get_connection() conn.request('GET', '/') + req_event.set() resp = conn.get_response() # Confirm the status code. @@ -958,6 +1004,8 @@ def socket_handler(listener): receive_preamble(sock) + # Wait for the message from the main thread. + send_event.wait() # Send the headers for the response. This response has no body. f = build_headers_frame( [(':status', '200'), ('content-length', '0')] @@ -965,9 +1013,6 @@ def socket_handler(listener): f.flags.add('END_STREAM') f.stream_id = 1 sock.sendall(f.serialize()) - - # Wait for the message from the main thread. - send_event.wait() sock.close() self._start_server(socket_handler) @@ -996,7 +1041,7 @@ def socket_handler(listener): data += sock.recv(65535) assert b'upgrade: h2c\r\n' in data - send_event.wait() + send_event.wait(5) # We need to send back a response. resp = ( @@ -1038,7 +1083,7 @@ class TestRequestsAdapter(SocketLevelTest): # This uses HTTP/2. h2 = True - def test_adapter_received_values(self, monkeypatch): + def test_adapter_received_values(self, monkeypatch, frame_buffer): self.set_up() # We need to patch the ssl_wrap_socket method to ensure that we @@ -1051,17 +1096,20 @@ def wrap(*args): monkeypatch.setattr(hyper.http11.connection, 'wrap_socket', wrap) - data = [] - send_event = threading.Event() - def socket_handler(listener): sock = listener.accept()[0] # Do the handshake: conn header, settings, send settings, recv ack. - receive_preamble(sock) + frame_buffer.add_data(receive_preamble(sock)) # Now expect some data. One headers frame. - data.append(sock.recv(65535)) + req_wait = True + while req_wait: + frame_buffer.add_data(sock.recv(65535)) + with reusable_frame_buffer(frame_buffer) as fr: + for f in fr: + if isinstance(f, HeadersFrame): + req_wait = False # Respond! h = HeadersFrame(1) @@ -1078,8 +1126,6 @@ def socket_handler(listener): d.data = b'1234567890' * 2 d.flags.add('END_STREAM') sock.send(d.serialize()) - - send_event.wait(5) sock.close() self._start_server(socket_handler) @@ -1093,11 +1139,9 @@ def socket_handler(listener): assert r.headers[b'Content-Type'] == b'not/real' assert r.content == b'1234567890' * 2 - send_event.set() - self.tear_down() - def test_adapter_sending_values(self, monkeypatch): + def test_adapter_sending_values(self, monkeypatch, frame_buffer): self.set_up() # We need to patch the ssl_wrap_socket method to ensure that we @@ -1110,17 +1154,22 @@ def wrap(*args): monkeypatch.setattr(hyper.http11.connection, 'wrap_socket', wrap) - data = [] + send_event = threading.Event() def socket_handler(listener): sock = listener.accept()[0] # Do the handshake: conn header, settings, send settings, recv ack. - receive_preamble(sock) + frame_buffer.add_data(receive_preamble(sock)) # Now expect some data. One headers frame and one data frame. - data.append(sock.recv(65535)) - data.append(sock.recv(65535)) + req_wait = True + while req_wait: + frame_buffer.add_data(sock.recv(65535)) + with reusable_frame_buffer(frame_buffer) as fr: + for f in fr: + if isinstance(f, DataFrame): + req_wait = False # Respond! h = HeadersFrame(1) @@ -1137,7 +1186,6 @@ def socket_handler(listener): d.data = b'1234567890' * 2 d.flags.add('END_STREAM') sock.send(d.serialize()) - sock.close() self._start_server(socket_handler) @@ -1152,11 +1200,10 @@ def socket_handler(listener): # Assert about the sent values. assert r.status_code == 200 - f = decode_frame(data[0]) - assert isinstance(f, HeadersFrame) + frames = list(frame_buffer) + assert isinstance(frames[-2], HeadersFrame) - f = decode_frame(data[1]) - assert isinstance(f, DataFrame) - assert f.data == b'hi there' + assert isinstance(frames[-1], DataFrame) + assert frames[-1].data == b'hi there' self.tear_down() From 6c56865e5a3e2aff243afb53eb6093f635d3b392 Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Thu, 23 Feb 2017 06:47:10 +0000 Subject: [PATCH 11/11] remove unused variable --- test/test_integration.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_integration.py b/test/test_integration.py index 4bffa641..f78107aa 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -1154,8 +1154,6 @@ def wrap(*args): monkeypatch.setattr(hyper.http11.connection, 'wrap_socket', wrap) - send_event = threading.Event() - def socket_handler(listener): sock = listener.accept()[0]