def test_window_increments_appropriately(self): e = Encoder() h = HeadersFrame(1) h.data = e.encode({':status': 200, 'content-type': 'foo/bar'}) h.flags = set(['END_HEADERS']) d = DataFrame(1) d.data = b'hi there sir' d2 = DataFrame(1) d2.data = b'hi there sir again' d2.flags = set(['END_STREAM']) sock = DummySocket() sock.buffer = BytesIO(h.serialize() + d.serialize() + d2.serialize()) c = HTTP20Connection('www.google.com') c._sock = sock c.window_manager.window_size = 1000 c.window_manager.initial_window_size = 1000 c.request('GET', '/') resp = c.get_response() resp.read() queue = list(map(decode_frame, map(memoryview, sock.queue))) assert len(queue) == 3 # one headers frame, two window update frames. assert isinstance(queue[1], WindowUpdateFrame) assert queue[1].window_increment == len(b'hi there sir') assert isinstance(queue[2], WindowUpdateFrame) assert queue[2].window_increment == len(b'hi there sir again')
def test_can_receive_trailers(self): headers = [('a', 'b'), ('c', 'd'), (':status', '200')] trailers = [('e', 'f'), ('g', 'h')] s = Stream(1, None, None, None, None, FixedDecoder(headers), None) s.state = STATE_HALF_CLOSED_LOCAL # Provide the first HEADERS frame. f = HeadersFrame(1) f.data = b'hi there!' f.flags.add('END_HEADERS') s.receive_frame(f) assert s.response_headers == HTTPHeaderMap(headers) # Now, replace the dummy decoder to ensure we get a new header block. s._decoder = FixedDecoder(trailers) # Provide the trailers. f = HeadersFrame(1) f.data = b'hi there again!' f.flags.add('END_STREAM') f.flags.add('END_HEADERS') s.receive_frame(f) # Now, check the trailers. assert s.response_trailers == HTTPHeaderMap(trailers) # Confirm we closed the stream. assert s.state == STATE_CLOSED
def socket_handler(listener): sock = listener.accept()[0] # Do the handshake: conn header, settings, send settings, recv ack. receive_preamble(sock) # Now expect some data. One headers frame and one data frame. data.append(sock.recv(65535)) data.append(sock.recv(65535)) # Respond! h = HeadersFrame(1) h.data = self.get_encoder().encode({ ':status': 200, 'Content-Type': 'not/real', 'Content-Length': 20 }) h.flags.add('END_HEADERS') sock.send(h.serialize()) d = DataFrame(1) d.data = b'1234567890' * 2 d.flags.add('END_STREAM') sock.send(d.serialize()) send_event.set() sock.close()
def add_headers_frame(self, stream_id, headers, end_block=True, end_stream=False): frame = HeadersFrame(stream_id) frame.data = self.encoder.encode(headers) if end_block: frame.flags.add('END_HEADERS') if end_stream: frame.flags.add('END_STREAM') self.frames.append(frame)
def build_headers_frame(headers, encoder=None): f = HeadersFrame(1) e = encoder if e is None: e = Encoder() e.huffman_coder = HuffmanEncoder(REQUEST_CODES, REQUEST_CODES_LENGTH) f.data = e.encode(headers) f.flags.add('END_HEADERS') return f
def start_request(self): """ Open the stream. Does this by encoding and sending the headers: no more calls to ``add_header`` are allowed after this method is called. The `end` flag controls whether this will be the end of the stream, or whether data will follow. """ # Strip any headers invalid in H2. #headers = h2_safe_headers(self.request_headers) host = self.connection.get_host(self.task.host) self.add_header(":method", self.task.method) self.add_header(":scheme", "https") self.add_header(":authority", host) self.add_header(":path", self.task.path) default_headers = (':method', ':scheme', ':authority', ':path') #headers = h2_safe_headers(self.task.headers) for name, value in list(self.task.headers.items()): is_default = to_native_string(name) in default_headers self.add_header(name, value, replace=is_default) # Encode the headers. encoded_headers = self._encoder(self.request_headers) # It's possible that there is a substantial amount of data here. The # data needs to go into one HEADERS frame, followed by a number of # CONTINUATION frames. For now, for ease of implementation, let's just # assume that's never going to happen (16kB of headers is lots!). # Additionally, since this is so unlikely, there's no point writing a # test for this: it's just so simple. if len(encoded_headers) > FRAME_MAX_LEN: # pragma: no cover raise ValueError("Header block too large.") header_frame = HeadersFrame(self.stream_id) header_frame.data = encoded_headers # If no data has been provided, this is the end of the stream. Either # way, due to the restriction above it's definitely the end of the # headers. header_frame.flags.add('END_HEADERS') if self.request_body_left == 0: header_frame.flags.add('END_STREAM') # Send the header frame. self.task.set_state("start send header") self._send_cb(header_frame) # Transition the stream state appropriately. self.state = STATE_OPEN self.task.set_state("start send left body") if self.request_body_left > 0: self.send_left_body()
def start_request(self): """ Open the stream. Does this by encoding and sending the headers: no more calls to ``add_header`` are allowed after this method is called. The `end` flag controls whether this will be the end of the stream, or whether data will follow. """ # Strip any headers invalid in H2. #headers = h2_safe_headers(self.request_headers) host = self.connection.get_host(self.task.host) self.add_header(":Method", self.task.method) self.add_header(":Scheme", "https") self.add_header(":Authority", host) self.add_header(":Path", self.task.path) default_headers = (':method', ':scheme', ':authority', ':path') #headers = h2_safe_headers(self.task.headers) for name, value in self.task.headers.items(): is_default = to_native_string(name) in default_headers self.add_header(name, value, replace=is_default) # Encode the headers. encoded_headers = self._encoder(self.request_headers) # It's possible that there is a substantial amount of data here. The # data needs to go into one HEADERS frame, followed by a number of # CONTINUATION frames. For now, for ease of implementation, let's just # assume that's never going to happen (16kB of headers is lots!). # Additionally, since this is so unlikely, there's no point writing a # test for this: it's just so simple. if len(encoded_headers) > FRAME_MAX_LEN: # pragma: no cover raise ValueError("Header block too large.") header_frame = HeadersFrame(self.stream_id) header_frame.data = encoded_headers # If no data has been provided, this is the end of the stream. Either # way, due to the restriction above it's definitely the end of the # headers. header_frame.flags.add('END_HEADERS') # Send the header frame. self.task.set_state("start send header") self._send_cb(header_frame) # Transition the stream state appropriately. self.state = STATE_OPEN self.task.set_state("start send left body") self.send_left_body() self.task.set_state("end send left body") self.timeout_response()
def test_cannot_receive_three_header_blocks(self): first = [('a', 'b'), ('c', 'd'), (':status', '200')] s = Stream(1, None, None, None, None, FixedDecoder(first), None) s.state = STATE_HALF_CLOSED_LOCAL # Provide the first two header frames. f = HeadersFrame(1) f.data = b'hi there!' f.flags.add('END_HEADERS') s.receive_frame(f) f = HeadersFrame(1) f.data = b'hi there again!' f.flags.add('END_HEADERS') s.receive_frame(f) # Provide the third. This one blows up. f = HeadersFrame(1) f.data = b'hi there again!' f.flags.add('END_STREAM') f.flags.add('END_HEADERS') with pytest.raises(ProtocolError): s.receive_frame(f)
def test_can_receive_continuation_frame_after_end_stream(self): s = Stream(1, None, None, None, None, None, FlowControlManager(65535)) f = HeadersFrame(1) f.data = 'hi there' f.flags = set('END_STREAM') f2 = ContinuationFrame(1) f2.data = ' sir' f2.flags = set('END_HEADERS') s.receive_frame(f) s.receive_frame(f2)
def test_headers_with_continuation(self): e = Encoder() header_data = e.encode( {':status': 200, 'content-type': 'foo/bar', 'content-length': '0'} ) h = HeadersFrame(1) h.data = header_data[0:int(len(header_data)/2)] c = ContinuationFrame(1) c.data = header_data[int(len(header_data)/2):] c.flags |= set(['END_HEADERS', 'END_STREAM']) sock = DummySocket() sock.buffer = BytesIO(h.serialize() + c.serialize()) c = HTTP20Connection('www.google.com') c._sock = sock r = c.request('GET', '/') assert set(c.get_response(r).headers.iter_raw()) == set([(b'content-type', b'foo/bar'), (b'content-length', b'0')])
def socket_handler(listener): sock = listener.accept()[0] # Do the handshake: conn header, settings, send settings, recv ack. receive_preamble(sock) # Now expect some data. One headers frame. data.append(sock.recv(65535)) # Respond! h = HeadersFrame(1) h.data = self.get_encoder().encode({':status': 200, 'Content-Type': 'not/real', 'Content-Length': 20}) h.flags.add('END_HEADERS') sock.send(h.serialize()) d = DataFrame(1) d.data = b'1234567890' * 2 d.flags.add('END_STREAM') sock.send(d.serialize()) send_event.wait() sock.close()
def test_headers_with_continuation(self): e = Encoder() header_data = e.encode({ ':status': 200, 'content-type': 'foo/bar', 'content-length': '0' }) h = HeadersFrame(1) h.data = header_data[0:int(len(header_data) / 2)] c = ContinuationFrame(1) c.data = header_data[int(len(header_data) / 2):] c.flags |= set(['END_HEADERS', 'END_STREAM']) sock = DummySocket() sock.buffer = BytesIO(h.serialize() + c.serialize()) c = HTTP20Connection('www.google.com') c._sock = sock r = c.request('GET', '/') assert set(c.get_response(r).headers.iter_raw()) == set([ (b'content-type', b'foo/bar'), (b'content-length', b'0') ])
def socket_handler(listener): sock = listener.accept()[0] receive_preamble(sock) data.append(sock.recv(65535)) send_event.wait() h = HeadersFrame(1) h.data = self.get_encoder().encode({ ':status': 200, 'Content-Type': 'not/real', 'Content-Length': 14, 'Server': 'socket-level-server' }) h.flags.add('END_HEADERS') sock.send(h.serialize()) d = DataFrame(1) d.data = b'nsaislistening' d.flags.add('END_STREAM') sock.send(d.serialize()) sock.close()
def socket_handler(listener): sock = listener.accept()[0] receive_preamble(sock) data.append(sock.recv(65535)) send_event.wait() h = HeadersFrame(1) h.data = self.get_encoder().encode( {':status': 200, 'Content-Type': 'not/real', 'Content-Length': 14, 'Server': 'socket-level-server'} ) h.flags.add('END_HEADERS') sock.send(h.serialize()) d = DataFrame(1) d.data = b'nsaislistening' d.flags.add('END_STREAM') sock.send(d.serialize()) sock.close()
def test_reading_trailers_early_reads_all_data(self): in_frames = [] headers = [('a', 'b'), ('c', 'd'), (':status', '200')] trailers = [('e', 'f'), ('g', 'h')] def recv_cb(s): def inner(): s.receive_frame(in_frames.pop(0)) return inner s = Stream(1, None, None, None, None, FixedDecoder(headers), FlowControlManager(65535)) s._recv_cb = recv_cb(s) s.state = STATE_HALF_CLOSED_LOCAL # Provide the first HEADERS frame. f = HeadersFrame(1) f.data = b'hi there!' f.flags.add('END_HEADERS') in_frames.append(f) # Provide some data. f = DataFrame(1) f.data = b'testdata' in_frames.append(f) # Provide the trailers. f = HeadersFrame(1) f.data = b'hi there again!' f.flags.add('END_STREAM') f.flags.add('END_HEADERS') in_frames.append(f) # Begin by reading the first headers. assert s.getheaders() == HTTPHeaderMap(headers) # Now, replace the dummy decoder to ensure we get a new header block. s._decoder = FixedDecoder(trailers) # Ask for the trailers. This should also read the data frames. assert s.gettrailers() == HTTPHeaderMap(trailers) assert s.data == [b'testdata']
def test_read_headers_out_of_order(self): # If header blocks aren't decoded in the same order they're received, # regardless of the stream they belong to, the decoder state will become # corrupted. e = Encoder() h1 = HeadersFrame(1) h1.data = e.encode({':status': 200, 'content-type': 'foo/bar'}) h1.flags |= set(['END_HEADERS', 'END_STREAM']) h3 = HeadersFrame(3) h3.data = e.encode({':status': 200, 'content-type': 'baz/qux'}) h3.flags |= set(['END_HEADERS', 'END_STREAM']) sock = DummySocket() sock.buffer = BytesIO(h1.serialize() + h3.serialize()) c = HTTP20Connection('www.google.com') c._sock = sock r1 = c.request('GET', '/a') r3 = c.request('GET', '/b') assert c.get_response(r3).headers == HTTPHeaderMap([('content-type', 'baz/qux')]) assert c.get_response(r1).headers == HTTPHeaderMap([('content-type', 'foo/bar')])