diff --git a/CHANGELOG.md b/CHANGELOG.md index c7e063e97a..bb3b7972e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ ([#5548](https://github.com/mitmproxy/mitmproxy/pull/5548), @sanlengjingvv) * Fix a mitmweb crash when scrolling down the flow list. ([#5507](https://github.com/mitmproxy/mitmproxy/pull/5507), @LIU-shuyi) +* Fix query editing on mitmweb. + ([#5574](https://github.com/mitmproxy/mitmproxy/pull/5574), @yesmeck) ## 28 June 2022: mitmproxy 8.1.1 diff --git a/mitmproxy/tools/web/app.py b/mitmproxy/tools/web/app.py index 1947dcc2fc..aad741f8f9 100644 --- a/mitmproxy/tools/web/app.py +++ b/mitmproxy/tools/web/app.py @@ -392,6 +392,8 @@ def put(self, flow_id): request.trailers.add(*trailer) elif k == "content": request.text = v + elif k == "query": + request.query = [tuple(i) for i in v] else: raise APIError(400, f"Unknown update request.{k}: {v}") diff --git a/test/mitmproxy/tools/web/test_app.py b/test/mitmproxy/tools/web/test_app.py index 5c63404cc7..703c478169 100644 --- a/test/mitmproxy/tools/web/test_app.py +++ b/test/mitmproxy/tools/web/test_app.py @@ -162,6 +162,7 @@ async def make_master() -> webmaster.WebMaster: f = tflow.tflow(resp=True) f.id = "42" f.request.content = b"foo\nbar" + f.request.query = [("foo", "1")] f2 = tflow.tflow(ws=True, resp=True) f2.request.content = None f2.response.content = None @@ -296,6 +297,7 @@ def test_flow_update(self): upd = { "request": { "trailers": [("foo", "baz")], + "query": [("foo", "2")] }, "response": { "trailers": [("foo", "baz")], @@ -303,6 +305,7 @@ def test_flow_update(self): } assert self.put_json("/flows/42", upd).code == 200 assert f.request.trailers["foo"] == "baz" + assert f.request.query["foo"] == "2" f.revert() diff --git a/web/src/js/__tests__/components/contentviews/HttpMessageSpec.tsx b/web/src/js/__tests__/components/contentviews/HttpMessageSpec.tsx index fbb9d5ee84..4d2c311d7b 100644 --- a/web/src/js/__tests__/components/contentviews/HttpMessageSpec.tsx +++ b/web/src/js/__tests__/components/contentviews/HttpMessageSpec.tsx @@ -4,7 +4,20 @@ import HttpMessage, {ViewImage} from '../../../components/contentviews/HttpMessa import {fireEvent, render, screen, waitFor} from "../../test-utils" import fetchMock, {enableFetchMocks} from "jest-fetch-mock"; -jest.mock("../../../contrib/CodeMirror") +jest.mock("../../../contrib/CodeMirror", () => { + const React = require("react"); + return { + __esModule: true, + default: React.forwardRef((props, ref) => { + React.useImperativeHandle(ref, () => ({ + codeMirror: { + getValue: () => props.value + } + })); + return
{props.value}
+ }) + } +}) enableFetchMocks(); @@ -25,6 +38,11 @@ test("HttpMessage", async () => { description: "Raw", }), "raw content", + JSON.stringify({ + lines: Array(5).fill([["text", "rawdata"]]), + description: "Raw", + }), + "", JSON.stringify({ lines: Array(5).fill([["text", "rawdata"]]), description: "Raw", @@ -32,6 +50,7 @@ test("HttpMessage", async () => { ); const tflow = TFlow(); + tflow.request.method = "POST"; const {asFragment} = render(); await waitFor(() => screen.getAllByText("data")); expect(screen.queryByText('additional')).toBeNull(); @@ -50,6 +69,52 @@ test("HttpMessage", async () => { await waitFor(() => screen.getAllByText("rawdata")); expect(asFragment()).toMatchSnapshot(); + + fireEvent.click(screen.getByText("Edit")); + fireEvent.click(screen.getByText("Done")); + await waitFor(() => screen.getAllByText("rawdata")); + expect(asFragment()).toMatchSnapshot(); +}); + +test("HttpMessage edit query string", async () => { + const lines = [ + [ + ["header", "foo"], + ["text", "1"], + ], + [ + ["header", "bar"], + ["text", "2"], + ], + ]; + + fetchMock.mockResponses( + JSON.stringify({ + lines: lines, + description: "Query", + }), + JSON.stringify({ + lines, + description: "Query", + }), + "", + JSON.stringify({ + lines, + description: "Query", + }) + ); + + const tflow = TFlow(); + tflow.request.path = "/path?foo=1&bar=2"; + const { asFragment } = render( + + ); + fireEvent.click(screen.getByText("Edit")); + expect(asFragment()).toMatchSnapshot(); + fireEvent.click(screen.getByText("Done")); + + await waitFor(() => screen.getAllByText("foo")); + expect(asFragment()).toMatchSnapshot(); }); test("ViewImage", async () => { diff --git a/web/src/js/__tests__/components/contentviews/__snapshots__/HttpMessageSpec.tsx.snap b/web/src/js/__tests__/components/contentviews/__snapshots__/HttpMessageSpec.tsx.snap index b79c50132b..e5014d4098 100644 --- a/web/src/js/__tests__/components/contentviews/__snapshots__/HttpMessageSpec.tsx.snap +++ b/web/src/js/__tests__/components/contentviews/__snapshots__/HttpMessageSpec.tsx.snap @@ -126,7 +126,11 @@ exports[`HttpMessage 2`] = `
+ > +
+ {"lines":[[["text","rawdata"]],[["text","rawdata"]],[["text","rawdata"]],[["text","rawdata"]],[["text","rawdata"]]],"description":"Raw"} +
+
`; @@ -226,6 +230,189 @@ exports[`HttpMessage 3`] = ` `; +exports[`HttpMessage 4`] = ` + +
+
+
+ Loading... +
+ +   + + + Replace + + +   + + + +   + + View: + + raw + + + +
+
+
+`; + +exports[`HttpMessage edit query string 1`] = ` + +
+
+
+ [Editing] +
+ +   + +
+
+
+ foo=1 +bar=2 +
+
+
+
+`; + +exports[`HttpMessage edit query string 2`] = ` + +
+
+
+ Query +
+ +   + + + Replace + + +   + + + +   + + View: + + auto + + + +
+
+      
+ + foo + + + 1 + +
+
+ + bar + + + 2 + +
+
+
+
+`; + exports[`ViewImage 1`] = `
[...acc, `${key}=${value}`], []) + .join("\n"); + } const contentViewData = useMemo(() => { if (content && !edit) { try { @@ -48,8 +54,14 @@ export default function HttpMessage({flow, message}: HttpMessageProps) { if (edit) { const save = async () => { - const content = editorRef.current?.getContent(); - await dispatch(flowActions.update(flow, {[part]: {content}})); + let content = editorRef.current?.getContent(); + await dispatch(flowActions.update(flow, { + [part]: flow.request.method === 'GET' ? { + query: content?.split("\n").map(item => item.split('=')) + }: { + content + } + })); setEdit(false); } return (