From b732a1cb3d1a31e0827de60092badfe162e920a3 Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Sat, 1 Aug 2015 14:36:31 +0200 Subject: reimplement streaming for HTTP/1 --- libmproxy/flow.py | 3 +- libmproxy/protocol/http.py | 109 +++++++++++++++++++++++---------------------- libmproxy/proxy/server.py | 6 +-- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/libmproxy/flow.py b/libmproxy/flow.py index 82a25461..3d9ef722 100644 --- a/libmproxy/flow.py +++ b/libmproxy/flow.py @@ -844,8 +844,7 @@ class FlowMaster(controller.Master): host, port, path, - (1, - 1), + (1, 1), headers, None, None, diff --git a/libmproxy/protocol/http.py b/libmproxy/protocol/http.py index b54d4a11..a85a4f30 100644 --- a/libmproxy/protocol/http.py +++ b/libmproxy/protocol/http.py @@ -176,10 +176,10 @@ class HTTPHandler(ProtocolHandler): # Only get the headers at first... flow.response = HTTPResponse.from_protocol( - flow.server_conn.protocol, + self.c.server_conn.protocol, flow.request.method, body_size_limit=self.c.config.body_size_limit, - include_body=False + include_body=False, ) break except (tcp.NetLibError, http.HttpErrorConnClosed) as v: @@ -214,9 +214,9 @@ class HTTPHandler(ProtocolHandler): if flow.response.stream: flow.response.content = CONTENT_MISSING else: - if isinstance(flow.server_conn.protocol, http1.HTTP1Protocol): + if isinstance(self.c.server_conn.protocol, http1.HTTP1Protocol): # streaming is only supported with HTTP/1 at the moment - flow.response.content = flow.server_conn.protocol.read_http_body( + flow.response.content = self.c.server_conn.protocol.read_http_body( flow.response.headers, self.c.config.body_size_limit, flow.request.method, @@ -369,43 +369,42 @@ class HTTPHandler(ProtocolHandler): flow.error = Error(message or message_debug) self.c.channel.ask("error", flow) try: - code = getattr(error, "code", 502) + status_code = getattr(error, "code", 502) headers = getattr(error, "headers", None) html_message = message or "" if message_debug: html_message += "
%s
" % message_debug - self.send_error(code, html_message, headers) + self.send_error(status_code, html_message, headers) except: pass - def send_error(self, code, message, headers): - # TODO: implement this again - raise NotImplementedError("todo - adapt for HTTP/2 - make use of make_error_reponse from pathod") - # response = http.status_codes.RESPONSES.get(code, "Unknown") - # html_content = """ - # - # - # %d %s - # - # %s - # - # """ % (code, response, message) - # self.c.client_conn.wfile.write("HTTP/1.1 %s %s\r\n" % (code, response)) - # self.c.client_conn.wfile.write( - # "Server: %s\r\n" % self.c.config.server_version - # ) - # self.c.client_conn.wfile.write("Content-type: text/html\r\n") - # self.c.client_conn.wfile.write( - # "Content-Length: %d\r\n" % len(html_content) - # ) - # if headers: - # for key, value in headers.items(): - # self.c.client_conn.wfile.write("%s: %s\r\n" % (key, value)) - # self.c.client_conn.wfile.write("Connection: close\r\n") - # self.c.client_conn.wfile.write("\r\n") - # self.c.client_conn.wfile.write(html_content) - # self.c.client_conn.wfile.flush() + def send_error(self, status_code, message, headers): + response = http.status_codes.RESPONSES.get(status_code, "Unknown") + body = """ + + + %d %s + + %s + + """ % (status_code, response, message) + + if not headers: + headers = odict.ODictCaseless() + headers["Server"] = [self.c.config.server_version] + headers["Connection"] = ["close"] + headers["Content-Length"] = [len(body)] + headers["Content-Type"] = ["text/html"] + + resp = HTTPResponse( + (1, 1), + status_code, + '', + headers, + body, + ) + self.c.client_conn.send(self.c.client_conn.protocol.assemble(resp)) def process_request(self, flow, request): """ @@ -566,31 +565,33 @@ class HTTPHandler(ProtocolHandler): # send it to the client straight away. self.c.client_conn.send(self.c.client_conn.protocol.assemble(flow.response)) else: - raise NotImplementedError("HTTP streaming is currently not supported.") - # TODO: implement it according to new protocols and messages + if isinstance(self.c.client_conn.protocol, http2.HTTP2Protocol): + raise NotImplementedError("HTTP streaming with HTTP/2 is currently not supported.") + # streaming: # First send the headers and then transfer the response # incrementally: - # h = flow.response._assemble_head(preserve_transfer_encoding=True) - # self.c.client_conn.send(h) - # - # protocol = http1.HTTP1Protocol(rfile=self.c.server_conn.rfile) - # chunks = protocol.read_http_body_chunked( - # flow.response.headers, - # self.c.config.body_size_limit, - # flow.request.method, - # flow.response.code, - # False, - # 4096 - # ) - # if callable(flow.response.stream): - # chunks = flow.response.stream(chunks) - # for chunk in chunks: - # for part in chunk: - # self.c.client_conn.wfile.write(part) - # self.c.client_conn.wfile.flush() - # flow.response.timestamp_end = utils.timestamp() + h = self.c.client_conn.protocol._assemble_response_first_line(flow.response) + self.c.client_conn.send(h + "\r\n") + h = self.c.client_conn.protocol._assemble_response_headers(flow.response, preserve_transfer_encoding=True) + self.c.client_conn.send(h + "\r\n\r\n") + + chunks = self.c.server_conn.protocol.read_http_body_chunked( + flow.response.headers, + self.c.config.body_size_limit, + flow.request.method, + flow.response.code, + False, + 4096 + ) + if callable(flow.response.stream): + chunks = flow.response.stream(chunks) + for chunk in chunks: + for part in chunk: + self.c.client_conn.wfile.write(part) + self.c.client_conn.wfile.flush() + flow.response.timestamp_end = utils.timestamp() def check_close_connection(self, flow): """ diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py index e77439fb..2f6ee061 100644 --- a/libmproxy/proxy/server.py +++ b/libmproxy/proxy/server.py @@ -117,7 +117,7 @@ class ConnectionHandler: self.server_conn.address(), "info") self.conntype = "tcp" - + elif not self.server_conn and self.config.mode == "sslspoof": port = self.config.mode.sslport self.set_server_address(("-", port)) @@ -243,7 +243,7 @@ class ConnectionHandler: ssl_cert_err = self.server_conn.ssl_verification_error if ssl_cert_err is not None: self.log( - "SSL verification failed for upstream server at depth %s with error: %s" % + "SSL verification failed for upstream server at depth %s with error: %s" % (ssl_cert_err['depth'], ssl_cert_err['errno']), "error") self.log("Ignoring server verification error, continuing with connection", "error") @@ -259,7 +259,7 @@ class ConnectionHandler: ssl_cert_err = self.server_conn.ssl_verification_error if ssl_cert_err is not None: self.log( - "SSL verification failed for upstream server at depth %s with error: %s" % + "SSL verification failed for upstream server at depth %s with error: %s" % (ssl_cert_err['depth'], ssl_cert_err['errno']), "error") self.log("Aborting connection attempt", "error") -- cgit v1.2.3