def test_parse_chunked_data(self): # See: https://en.wikipedia.org/wiki/Chunked_transfer_encoding chunked = '4\r\nWiki\r\n5\r\npedia\r\nE\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n' expected = 'Wikipedia in\r\n\r\nchunks.' # test parsing parsed = parse_chunked_data(chunked) self.assertEqual(expected.strip(), parsed.strip()) # test roundtrip chunked_computed = create_chunked_data(expected) parsed = parse_chunked_data(chunked_computed) self.assertEqual(expected.strip(), parsed.strip())
def forward(self, method): data = self.data_bytes path = self.path forward_headers = CaseInsensitiveDict(self.headers) # force close connection connection_header = forward_headers.get('Connection') or '' if connection_header.lower() not in ['keep-alive', '']: self.close_connection = 1 client_address = self.client_address[0] server_address = ':'.join(map(str, self.server.server_address)) try: # run the actual response forwarding response = modify_and_forward( method=method, path=path, data_bytes=data, headers=forward_headers, forward_base_url=self.proxy.forward_base_url, listeners=self._listeners(), request_handler=self, client_address=client_address, server_address=server_address) # copy headers and return response self.send_response(response.status_code) # set content for chunked encoding is_chunked = uses_chunked_encoding(response) if is_chunked: response._content = create_chunked_data(response._content) # send headers content_length_sent = False for header_key, header_value in iteritems(response.headers): # filter out certain headers that we don't want to transmit if header_key.lower() not in ('transfer-encoding', 'date', 'server'): self.send_header(header_key, header_value) content_length_sent = content_length_sent or header_key.lower( ) == 'content-length' # fix content-type header if needed if not content_length_sent and not is_chunked: self.send_header( 'Content-Length', '%s' % len(response.content) if response.content else 0) if isinstance(response, LambdaResponse): self.send_multi_value_headers(response.multi_value_headers) self.end_headers() if response.content and len(response.content): self.wfile.write(to_bytes(response.content)) except Exception as e: trace = str(traceback.format_exc()) conn_errors = ('ConnectionRefusedError', 'NewConnectionError', 'Connection aborted', 'Unexpected EOF', 'Connection reset by peer', 'cannot read from timed out object') conn_error = any(e in trace for e in conn_errors) error_msg = 'Error forwarding request: %s %s' % (e, trace) if 'Broken pipe' in trace: LOG.warn( 'Connection prematurely closed by client (broken pipe).') elif not self.proxy.quiet or not conn_error: LOG.error(error_msg) if is_local_test_mode(): # During a test run, we also want to print error messages, because # log messages are delayed until the entire test run is over, and # hence we are missing messages if the test hangs for some reason. print('ERROR: %s' % error_msg) self.send_response(502) # bad gateway self.end_headers() # force close connection self.close_connection = 1 finally: try: self.wfile.flush() except Exception as e: LOG.warning('Unable to flush write file: %s' % e)