def replace_boundary(req: Request): fixed_boundary = 'fixed_boundary' content_type_header, old_boundary = req.headers['Content-Type'].split( 'boundary=') req.headers['Content-Type'] = 'boundary='.join( [content_type_header, fixed_boundary]) req.content = req.content.replace(old_boundary.encode(), fixed_boundary.encode())
def clean_multipart_form(self, req: Request) -> None: """Replace any problematic values of multipart form keys with constant data Args: req (Request): The request to modify """ if req.multipart_form and self.form_keys: updated_multipart_form_data = [] for key, val in req.multipart_form.items(multi=True): if key in self.form_keys: updated_multipart_form_data.append((key, self.constant)) else: updated_multipart_form_data.append((key, val)) req._set_multipart_form(updated_multipart_form_data)
def test_no_data_on_closed_stream(self, tctx): frame_factory = FrameFactory() req = Request.make("GET", "http://example.com/") resp = {":status": 200} assert ( Playbook(Http2Client(tctx)) << SendData( tctx.server, Placeholder(bytes)) # preamble + initial settings frame >> DataReceived( tctx.server, frame_factory.build_settings_frame({}, ack=True).serialize()) >> http.RequestHeaders(1, req, end_stream=True) << SendData( tctx.server, b"\x00\x00\x06\x01\x05\x00\x00\x00\x01\x82\x86\x84\\\x81\x07") >> http.RequestEndOfMessage(1) >> DataReceived( tctx.server, frame_factory.build_headers_frame(resp).serialize()) << http.ReceiveHttp(Placeholder( http.ResponseHeaders)) >> http.RequestProtocolError( 1, "cancelled", code=status_codes.CLIENT_CLOSED_REQUEST) << SendData( tctx.server, frame_factory.build_rst_stream_frame( 1, ErrorCodes.CANCEL).serialize()) >> DataReceived( tctx.server, frame_factory.build_data_frame(b"foo").serialize()) << SendData( tctx.server, frame_factory.build_rst_stream_frame( 1, ErrorCodes.STREAM_CLOSED).serialize()) ) # important: no ResponseData event here!
def read_request_head(lines: List[bytes]) -> Request: """ Parse an HTTP request head (request line + headers) from an iterable of lines Args: lines: The input lines Returns: The HTTP request object (without body) Raises: ValueError: The input is malformed. """ host, port, method, scheme, authority, path, http_version = _read_request_line(lines[0]) headers = _read_headers(lines[1:]) return Request( host=host, port=port, method=method, scheme=scheme, authority=authority, path=path, http_version=http_version, headers=headers, content=None, trailers=None, timestamp_start=time.time(), timestamp_end=None )
def modify_json_body(self, req: Request, json_body: dict) -> None: """Modify the json body of a request by replacing any timestamp data with constant data Args: req (Request): The request whose json body will be modified. json_body (dict): The request body to modify. """ original_content = deepcopy(json_body) modified = False keys_to_replace = self.json_keys logging.info('{}'.format(keys_to_replace)) for key_path in keys_to_replace: body = json_body keys = key_path.split('.') logging.info('keypath parts: {}'.format(keys)) lastkey = keys[-1] logging.info('lastkey: {}'.format(lastkey)) skip_key = False for k in keys[:-1]: if k in body: body = body[k] elif isinstance(body, list) and k.isdigit(): if int(k) > len(body) - 1: skip_key = True break body = body[int(k)] else: skip_key = True break if not skip_key: if lastkey in body: logging.info('modifying request to "{}"'.format( req.pretty_url)) body[lastkey] = self.constant modified = True elif isinstance(body, list) and lastkey.isdigit( ) and int(lastkey) <= len(body) - 1: logging.info('modifying request to "{}"'.format( req.pretty_url)) body[int(lastkey)] = self.constant modified = True if modified: logging.info('original request body:\n{}'.format( json.dumps(original_content, indent=4))) logging.info('modified request body:\n{}'.format( json.dumps(json_body, indent=4))) req.set_content(json.dumps(json_body).encode())
def handle_url_query(self, req: Request) -> None: query_data = req._get_query() logging.info('query_data: {}'.format(query_data)) for key, val in query_data: # don't bother trying to interpret an argument less than 4 characters as some type of timestamp if len(val) > 4: if self.safely_parse(val): self.query_keys.add(key)
def clean_url_query(self, req: Request) -> None: """Replace any problematic values of query parameters with constant data Args: req (Request): The request to modify """ query_data = sorted(req._get_query()) logging.info('fetched query_data: {}'.format(query_data)) updated_query_data = [] if query_data and self.query_keys: for key, val in query_data: if key in self.query_keys: updated_query_data.append((key, self.constant)) else: updated_query_data.append((key, val)) req._set_query(updated_query_data or query_data) logging.info(f'updated query_data: {req._get_query()}')
def request_content_for_console(request: http.Request) -> str: try: text = request.get_text(strict=True) assert text except ValueError: # shlex.quote doesn't support a bytes object # see https://github.com/python/cpython/pull/10871 raise exceptions.CommandError("Request content must be valid unicode") escape_control_chars = {chr(i): f"\\x{i:02x}" for i in range(32)} return "".join(escape_control_chars.get(x, x) for x in text)
def test_init_conv(self): assert Request( b"example.com", 80, "GET", "http", "example.com", "/", "HTTP/1.1", (), None, (), 0, 0, ) # type: ignore
def flow(): request = Request(host=b'localhost', path=b'/test/', http_version=b'1.1', port=1234, method=b'', scheme=b'', headers=Headers([(b"Host", b"example.com")]), content=None, timestamp_start=111.1, timestamp_end=111.2, authority=b'', trailers='') flow = HTTPFlow(client_conn=MagicMock(), server_conn=MagicMock()) flow.request = request return flow
def ws_testdata(tctx): tctx.server.address = ("example.com", 80) tctx.server.state = ConnectionState.OPEN flow = HTTPFlow( tctx.client, tctx.server ) flow.request = Request.make("GET", "http://example.com/", headers={ "Connection": "upgrade", "Upgrade": "websocket", "Sec-WebSocket-Version": "13", }) flow.response = Response.make(101, headers={ "Connection": "upgrade", "Upgrade": "websocket", }) return tctx, Playbook(websocket.WebsocketLayer(tctx, flow))
def test_make(self): r = Request.make("GET", "https://example.com/") assert r.method == "GET" assert r.scheme == "https" assert r.host == "example.com" assert r.port == 443 assert r.path == "/" r = Request.make("GET", "https://example.com/", "content", {"Foo": "bar"}) assert r.content == b"content" assert r.headers["content-length"] == "7" assert r.headers["Foo"] == "bar" Request.make("GET", "https://example.com/", content=b"content") with pytest.raises(TypeError): Request.make("GET", "https://example.com/", content=42) r = Request.make("GET", "https://example.com/", headers=[(b"foo", b"bar")]) assert r.headers["foo"] == "bar" r = Request.make("GET", "https://example.com/", headers=({ "foo": "baz" })) assert r.headers["foo"] == "baz" r = Request.make("GET", "https://example.com/", headers=Headers(foo="qux")) assert r.headers["foo"] == "qux" with pytest.raises(TypeError): Request.make("GET", "https://example.com/", headers=42)