def validate_request(self, request): if request.form_in == "absolute" and request.scheme != "http": raise HttpException("Invalid request scheme: %s" % request.scheme) expected_request_forms = { "regular": ("authority", "absolute",), "upstream": ("authority", "absolute"), "transparent": ("relative",) } allowed_request_forms = expected_request_forms[self.mode] if request.form_in not in allowed_request_forms: err_message = "Invalid HTTP request form (expected: %s, got: %s)" % ( " or ".join(allowed_request_forms), request.form_in ) raise HttpException(err_message) if self.mode == "regular" and request.form_in == "absolute": request.form_out = "relative"
def http_connect(self, connect_to): self.wfile.write('CONNECT %s:%s HTTP/1.1\r\n' % tuple(connect_to) + '\r\n') self.wfile.flush() try: resp = self.protocol.read_response(self.rfile, treq(method="CONNECT")) if resp.status_code != 200: raise HttpException("Unexpected status code: %s" % resp.status_code) except HttpException as e: six.reraise(PathocError, PathocError("Proxy CONNECT failed: %s" % repr(e)))
def read_body(io, expected_size): """ Read a (malformed) HTTP body. Returns: A (body: bytes, is_malformed: bool) tuple. """ body_start = io.tell() try: content = b"".join(http1.read_body(io, expected_size, None)) if io.read(): # leftover? raise HttpException() return content, False except HttpException: io.seek(body_start) return io.read(), True
def send_response(self, response): if response.content == CONTENT_MISSING: raise HttpException("Cannot assemble flow with CONTENT_MISSING") self.send_response_headers(response) self.send_response_body(response, [response.content])
def _handle_event(self, event, source_conn, other_conn, is_server): self.log( "HTTP2 Event from {}".format("server" if is_server else "client"), "debug", [repr(event)] ) if hasattr(event, 'stream_id'): if is_server and event.stream_id % 2 == 1: eid = self.server_to_client_stream_ids[event.stream_id] else: eid = event.stream_id if isinstance(event, events.RequestReceived): headers = Headers([[k, v] for k, v in event.headers]) self.streams[eid] = Http2SingleStreamLayer(self, eid, headers) self.streams[eid].timestamp_start = time.time() self.streams[eid].start() elif isinstance(event, events.ResponseReceived): headers = Headers([[k, v] for k, v in event.headers]) self.streams[eid].queued_data_length = 0 self.streams[eid].timestamp_start = time.time() self.streams[eid].response_headers = headers self.streams[eid].response_arrived.set() elif isinstance(event, events.DataReceived): if self.config.body_size_limit and self.streams[eid].queued_data_length > self.config.body_size_limit: raise HttpException("HTTP body too large. Limit is {}.".format(self.config.body_size_limit)) self.streams[eid].data_queue.put(event.data) self.streams[eid].queued_data_length += len(event.data) source_conn.h2.safe_increment_flow_control(event.stream_id, event.flow_controlled_length) elif isinstance(event, events.StreamEnded): self.streams[eid].timestamp_end = time.time() self.streams[eid].data_finished.set() elif isinstance(event, events.StreamReset): self.streams[eid].zombie = time.time() if eid in self.streams and event.error_code == 0x8: if is_server: other_stream_id = self.streams[eid].client_stream_id else: other_stream_id = self.streams[eid].server_stream_id if other_stream_id is not None: other_conn.h2.safe_reset_stream(other_stream_id, event.error_code) elif isinstance(event, events.RemoteSettingsChanged): new_settings = dict([(id, cs.new_value) for (id, cs) in six.iteritems(event.changed_settings)]) other_conn.h2.safe_update_settings(new_settings) elif isinstance(event, events.ConnectionTerminated): # Do not immediately terminate the other connection. # Some streams might be still sending data to the client. return False elif isinstance(event, events.PushedStreamReceived): # pushed stream ids should be unique and not dependent on race conditions # only the parent stream id must be looked up first parent_eid = self.server_to_client_stream_ids[event.parent_stream_id] with self.client_conn.h2.lock: self.client_conn.h2.push_stream(parent_eid, event.pushed_stream_id, event.headers) self.client_conn.send(self.client_conn.h2.data_to_send()) headers = Headers([[str(k), str(v)] for k, v in event.headers]) headers['x-mitmproxy-pushed'] = 'true' self.streams[event.pushed_stream_id] = Http2SingleStreamLayer(self, event.pushed_stream_id, headers) self.streams[event.pushed_stream_id].timestamp_start = time.time() self.streams[event.pushed_stream_id].pushed = True self.streams[event.pushed_stream_id].parent_stream_id = parent_eid self.streams[event.pushed_stream_id].timestamp_end = time.time() self.streams[event.pushed_stream_id].request_data_finished.set() self.streams[event.pushed_stream_id].start() elif isinstance(event, events.PriorityUpdated): stream_id = event.stream_id if stream_id in self.streams.keys() and self.streams[stream_id].server_stream_id: stream_id = self.streams[stream_id].server_stream_id depends_on = event.depends_on if depends_on in self.streams.keys() and self.streams[depends_on].server_stream_id: depends_on = self.streams[depends_on].server_stream_id # weight is between 1 and 256 (inclusive), but represented as uint8 (0 to 255) frame = PriorityFrame(stream_id, depends_on, event.weight - 1, event.exclusive) self.server_conn.send(frame.serialize()) elif isinstance(event, events.TrailersReceived): raise NotImplementedError() return True
def _handle_event(self, event, source_conn, other_conn, is_server): if hasattr(event, 'stream_id'): if is_server and event.stream_id % 2 == 1: eid = self.server_to_client_stream_ids[event.stream_id] else: eid = event.stream_id if isinstance(event, RequestReceived): headers = Headers([[str(k), str(v)] for k, v in event.headers]) self.streams[eid] = Http2SingleStreamLayer(self, eid, headers) self.streams[eid].timestamp_start = time.time() self.streams[eid].start() elif isinstance(event, ResponseReceived): headers = Headers([[str(k), str(v)] for k, v in event.headers]) self.streams[eid].queued_data_length = 0 self.streams[eid].timestamp_start = time.time() self.streams[eid].response_headers = headers self.streams[eid].response_arrived.set() elif isinstance(event, DataReceived): if self.config.body_size_limit and self.streams[ eid].queued_data_length > self.config.body_size_limit: raise HttpException("HTTP body too large. Limit is {}.".format( self.config.body_size_limit)) self.streams[eid].data_queue.put(event.data) self.streams[eid].queued_data_length += len(event.data) source_conn.h2.safe_increment_flow_control( event.stream_id, event.flow_controlled_length) elif isinstance(event, StreamEnded): self.streams[eid].timestamp_end = time.time() self.streams[eid].data_finished.set() elif isinstance(event, StreamReset): self.streams[eid].zombie = time.time() if eid in self.streams and event.error_code == 0x8: if is_server: other_stream_id = self.streams[eid].client_stream_id else: other_stream_id = self.streams[eid].server_stream_id if other_stream_id is not None: other_conn.h2.safe_reset_stream(other_stream_id, event.error_code) elif isinstance(event, RemoteSettingsChanged): new_settings = dict([ (id, cs.new_value) for (id, cs) in event.changed_settings.iteritems() ]) other_conn.h2.safe_update_settings(new_settings) elif isinstance(event, ConnectionTerminated): # Do not immediately terminate the other connection. # Some streams might be still sending data to the client. return False elif isinstance(event, PushedStreamReceived): # pushed stream ids should be uniq and not dependent on race conditions # only the parent stream id must be looked up first parent_eid = self.server_to_client_stream_ids[ event.parent_stream_id] with self.client_conn.h2.lock: self.client_conn.h2.push_stream(parent_eid, event.pushed_stream_id, event.headers) headers = Headers([[str(k), str(v)] for k, v in event.headers]) headers['x-mitmproxy-pushed'] = 'true' self.streams[event.pushed_stream_id] = Http2SingleStreamLayer( self, event.pushed_stream_id, headers) self.streams[event.pushed_stream_id].timestamp_start = time.time() self.streams[event.pushed_stream_id].pushed = True self.streams[event.pushed_stream_id].parent_stream_id = parent_eid self.streams[event.pushed_stream_id].timestamp_end = time.time() self.streams[event.pushed_stream_id].request_data_finished.set() self.streams[event.pushed_stream_id].start() elif isinstance(event, TrailersReceived): raise NotImplementedError() return True
def send_response(self, response): if response.content is None: raise HttpException("Cannot assemble flow with missing content") self.send_response_headers(response) self.send_response_body(response, [response.content])