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`] = `
+
+
+
+`;
+
+exports[`HttpMessage edit query string 1`] = `
+
+
+
+
+ [Editing]
+
+
+
+
+
+
+
+
+`;
+
+exports[`HttpMessage edit query string 2`] = `
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+ 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 (