def from_stream(cls, rfile, include_content=True, body_size_limit=None): """ Parse an HTTP request from a file stream """ httpversion, host, port, scheme, method, path, headers, content, timestamp_start, timestamp_end \ = None, None, None, None, None, None, None, None, None, None if hasattr(rfile, "reset_timestamps"): rfile.reset_timestamps() request_line = get_line(rfile) if hasattr(rfile, "first_byte_timestamp"): timestamp_start = rfile.first_byte_timestamp else: timestamp_start = utils.timestamp() request_line_parts = http.parse_init(request_line) if not request_line_parts: raise http.HttpError( 400, "Bad HTTP request line: %s" % repr(request_line)) method, path, httpversion = request_line_parts if path == '*': form_in = "asterisk" elif path.startswith("/"): form_in = "origin" if not netlib.utils.isascii(path): raise http.HttpError( 400, "Bad HTTP request line: %s" % repr(request_line)) elif method.upper() == 'CONNECT': form_in = "authority" r = http.parse_init_connect(request_line) if not r: raise http.HttpError( 400, "Bad HTTP request line: %s" % repr(request_line)) host, port, _ = r path = None else: form_in = "absolute" r = http.parse_init_proxy(request_line) if not r: raise http.HttpError( 400, "Bad HTTP request line: %s" % repr(request_line)) _, scheme, host, port, path, _ = r headers = http.read_headers(rfile) if headers is None: raise http.HttpError(400, "Invalid headers") if include_content: content = http.read_http_body(rfile, headers, body_size_limit, True) timestamp_end = utils.timestamp() return HTTPRequest(form_in, method, scheme, host, port, path, httpversion, headers, content, timestamp_start, timestamp_end)
def assemble_response(self, response): assert isinstance(response, semantics.Response) if response.body == semantics.CONTENT_MISSING: raise http.HttpError(502, "Cannot assemble flow with CONTENT_MISSING") first_line = self._assemble_response_first_line(response) headers = self._assemble_response_headers(response) return "%s\r\n%s\r\n%s" % (first_line, headers, response.body)
def process_request(self, flow, request): if not self.skip_authentication: self.authenticate(request) if request.form_in == "authority": if self.c.client_conn.ssl_established: raise http.HttpError( 400, "Must not CONNECT on already encrypted connection") if self.expected_form_in == "absolute": if not self.c.config.get_upstream_server: self.c.set_server_address( (request.host, request.port), proxy.AddressPriority.FROM_PROTOCOL) self.c.establish_server_connection() flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow self.c.client_conn.send( 'HTTP/1.1 200 Connection established\r\n' + 'Content-Length: 0\r\n' + ('Proxy-agent: %s\r\n' % self.c.server_version) + '\r\n') self.ssl_upgrade() self.skip_authentication = True return False else: return True elif request.form_in == self.expected_form_in: if request.form_in == "absolute": if request.scheme != "http": raise http.HttpError( 400, "Invalid request scheme: %s" % request.scheme) self.c.set_server_address((request.host, request.port), proxy.AddressPriority.FROM_PROTOCOL) flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow request.form_out = self.expected_form_out return True raise http.HttpError( 400, "Invalid HTTP request form (expected: %s, got: %s)" % (self.expected_form_in, request.form_in))
def process_request(self, flow, request): if self.c.mode == "regular": self.authenticate(request) if request.form_in == "authority" and self.c.client_conn.ssl_established: raise http.HttpError( 502, "Must not CONNECT on already encrypted connection") # If we have a CONNECT request, we might need to intercept if request.form_in == "authority": directly_addressed_at_mitmproxy = (self.c.mode == "regular" and not self.c.config.forward_proxy) if directly_addressed_at_mitmproxy: self.c.set_server_address((request.host, request.port), AddressPriority.FROM_PROTOCOL) flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow self.c.client_conn.wfile.write( 'HTTP/1.1 200 Connection established\r\n' + ('Proxy-agent: %s\r\n' % self.c.server_version) + '\r\n') self.c.client_conn.wfile.flush() self.ssl_upgrade() # raises ConnectionTypeChange exception if self.c.mode == "regular": if request.form_in == "authority": # forward mode self.hook_reconnect(request) elif request.form_in == "absolute": if request.scheme != "http": raise http.HttpError(400, "Invalid Request") if not self.c.config.forward_proxy: request.form_out = "origin" self.c.set_server_address((request.host, request.port), AddressPriority.FROM_PROTOCOL) flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow else: raise http.HttpError( 400, "Invalid request form (absolute-form or authority-form required)" )
def _assemble_first_line(self, form=None): form = form or self.form_out if form == "relative": path = self.path if self.method != "OPTIONS" else "*" request_line = '%s %s HTTP/%s.%s' % \ (self.method, path, self.httpversion[0], self.httpversion[1]) elif form == "authority": request_line = '%s %s:%s HTTP/%s.%s' % ( self.method, self.host, self.port, self.httpversion[0], self.httpversion[1]) elif form == "absolute": request_line = '%s %s://%s:%s%s HTTP/%s.%s' % \ (self.method, self.scheme, self.host, self.port, self.path, self.httpversion[0], self.httpversion[1]) else: raise http.HttpError(400, "Invalid request form") return request_line
def _assemble_first_line(self, form=None): form = form or self.form_out if form == "asterisk" or \ form == "origin": request_line = '%s %s HTTP/%s.%s' % (self.method, self.path, self.httpversion[0], self.httpversion[1]) elif form == "authority": request_line = '%s %s:%s HTTP/%s.%s' % ( self.method, self.host, self.port, self.httpversion[0], self.httpversion[1]) elif form == "absolute": request_line = '%s %s://%s:%s%s HTTP/%s.%s' % \ (self.method, self.scheme, self.host, self.port, self.path, self.httpversion[0], self.httpversion[1]) else: raise http.HttpError(400, "Invalid request form") return request_line
def test_httperror(): e = http.HttpError(404, "Not found") assert str(e)
def process_request(self, flow, request): """ @returns: True, if the request should not be sent upstream False, if the connection should be aborted None, if the request should be sent upstream (a status code != None should be returned directly by handle_flow) """ if not self.skip_authentication: self.authenticate(request) # Determine .scheme, .host and .port attributes # For absolute-form requests, they are directly given in the request. # For authority-form requests, we only need to determine the request scheme. # For relative-form requests, we need to determine host and port as well. if not request.scheme: request.scheme = "https" if flow.server_conn and flow.server_conn.ssl_established else "http" if not request.host: # Host/Port Complication: In upstream mode, use the server we CONNECTed to, # not the upstream proxy. if flow.server_conn: for s in flow.server_conn.state: if s[0] == "http" and s[1]["state"] == "connect": request.host, request.port = s[1]["host"], s[1]["port"] if not request.host and flow.server_conn: request.host, request.port = flow.server_conn.address.host, flow.server_conn.address.port # Now we can process the request. if request.form_in == "authority": if self.c.client_conn.ssl_established: raise http.HttpError( 400, "Must not CONNECT on already encrypted connection") if self.c.config.mode == "regular": self.c.set_server_address((request.host, request.port)) flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow self.c.establish_server_connection() self.c.client_conn.send( 'HTTP/1.1 200 Connection established\r\n' + 'Content-Length: 0\r\n' + ('Proxy-agent: %s\r\n' % self.c.config.server_version) + '\r\n') return self.process_connect_request(self.c.server_conn.address) elif self.c.config.mode == "upstream": return None else: pass # CONNECT should never occur if we don't expect absolute-form requests elif request.form_in == self.expected_form_in: request.form_out = self.expected_form_out if request.form_in == "absolute": if request.scheme != "http": raise http.HttpError( 400, "Invalid request scheme: %s" % request.scheme) if self.c.config.mode == "regular": # Update info so that an inline script sees the correct value at flow.server_conn self.c.set_server_address((request.host, request.port)) flow.server_conn = self.c.server_conn return None raise http.HttpError( 400, "Invalid HTTP request form (expected: %s, got: %s)" % (self.expected_form_in, request.form_in))
def process_request(self, flow, request): """ @returns: True, if the request should not be sent upstream False, if the connection should be aborted None, if the request should be sent upstream (a status code != None should be returned directly by handle_flow) """ if not self.skip_authentication: self.authenticate(request) # Determine .scheme, .host and .port attributes # For absolute-form requests, they are directly given in the request. # For authority-form requests, we only need to determine the request scheme. # For relative-form requests, we need to determine host and port as # well. if not request.scheme: request.scheme = "https" if flow.server_conn and flow.server_conn.ssl_established else "http" if not request.host: # Host/Port Complication: In upstream mode, use the server we CONNECTed to, # not the upstream proxy. if flow.server_conn: for s in flow.server_conn.state: if s[0] == "http" and s[1]["state"] == "connect": request.host, request.port = s[1]["host"], s[1]["port"] if not request.host and flow.server_conn: request.host, request.port = flow.server_conn.address.host, flow.server_conn.address.port # Now we can process the request. if request.form_in == "authority": if self.c.client_conn.ssl_established: raise http.HttpError( 400, "Must not CONNECT on already encrypted connection" ) if self.c.config.mode == "regular": self.c.set_server_address((request.host, request.port)) # Update server_conn attribute on the flow flow.server_conn = self.c.server_conn # since we currently only support HTTP/1 CONNECT requests # the response must be HTTP/1 as well self.c.client_conn.send( ('HTTP/%s.%s 200 ' % (request.httpversion[0], request.httpversion[1])) + 'Connection established\r\n' + 'Content-Length: 0\r\n' + ('Proxy-agent: %s\r\n' % self.c.config.server_version) + '\r\n' ) return self.process_connect_request(self.c.server_conn.address) elif self.c.config.mode == "upstream": return None else: # CONNECT should never occur if we don't expect absolute-form # requests pass elif request.form_in == self.expected_form_in: request.form_out = self.expected_form_out if request.form_in == "absolute": if request.scheme != "http": raise http.HttpError( 400, "Invalid request scheme: %s" % request.scheme ) if self.c.config.mode == "regular": # Update info so that an inline script sees the correct # value at flow.server_conn self.c.set_server_address((request.host, request.port)) flow.server_conn = self.c.server_conn elif request.form_in == "relative": if self.c.config.mode == "spoof": # Host header h = request.pretty_host(hostheader=True) if h is None: raise http.HttpError( 400, "Invalid request: No host information" ) p = netlib.utils.parse_url("http://" + h) request.scheme = p[0] request.host = p[1] request.port = p[2] self.c.set_server_address((request.host, request.port)) flow.server_conn = self.c.server_conn if self.c.config.mode == "sslspoof": # SNI is processed in server.py if not (flow.server_conn and flow.server_conn.ssl_established): raise http.HttpError( 400, "Invalid request: No host information" ) return None raise http.HttpError( 400, "Invalid HTTP request form (expected: %s, got: %s)" % ( self.expected_form_in, request.form_in ) )