From 438ad8148b7e1cdf81ec4803cd2fa08e4da15d21 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Fri, 8 Nov 2024 09:33:40 +0800 Subject: [PATCH] fix(http_request): send form data (#10431) --- .../workflow/nodes/http_request/executor.py | 22 +++--- .../test_http_request_executor.py | 69 +++++++++++++++++++ 2 files changed, 78 insertions(+), 13 deletions(-) diff --git a/api/core/workflow/nodes/http_request/executor.py b/api/core/workflow/nodes/http_request/executor.py index d90dfcc766..80b322b068 100644 --- a/api/core/workflow/nodes/http_request/executor.py +++ b/api/core/workflow/nodes/http_request/executor.py @@ -97,15 +97,6 @@ class Executor: headers = self.variable_pool.convert_template(self.node_data.headers).text self.headers = _plain_text_to_dict(headers) - body = self.node_data.body - if body is None: - return - if "content-type" not in (k.lower() for k in self.headers) and body.type in BODY_TYPE_TO_CONTENT_TYPE: - self.headers["Content-Type"] = BODY_TYPE_TO_CONTENT_TYPE[body.type] - if body.type == "form-data": - self.boundary = f"----WebKitFormBoundary{_generate_random_string(16)}" - self.headers["Content-Type"] = f"multipart/form-data; boundary={self.boundary}" - def _init_body(self): body = self.node_data.body if body is not None: @@ -154,9 +145,8 @@ class Executor: for k, v in files.items() if v.related_id is not None } - self.data = form_data - self.files = files + self.files = files or None def _assembling_headers(self) -> dict[str, Any]: authorization = deepcopy(self.auth) @@ -217,6 +207,7 @@ class Executor: "timeout": (self.timeout.connect, self.timeout.read, self.timeout.write), "follow_redirects": True, } + # request_args = {k: v for k, v in request_args.items() if v is not None} response = getattr(ssrf_proxy, self.method)(**request_args) return response @@ -244,6 +235,13 @@ class Executor: raw += f"Host: {url_parts.netloc}\r\n" headers = self._assembling_headers() + body = self.node_data.body + boundary = f"----WebKitFormBoundary{_generate_random_string(16)}" + if body: + if "content-type" not in (k.lower() for k in self.headers) and body.type in BODY_TYPE_TO_CONTENT_TYPE: + headers["Content-Type"] = BODY_TYPE_TO_CONTENT_TYPE[body.type] + if body.type == "form-data": + headers["Content-Type"] = f"multipart/form-data; boundary={boundary}" for k, v in headers.items(): if self.auth.type == "api-key": authorization_header = "Authorization" @@ -256,7 +254,6 @@ class Executor: body = "" if self.files: - boundary = self.boundary for k, v in self.files.items(): body += f"--{boundary}\r\n" body += f'Content-Disposition: form-data; name="{k}"\r\n\r\n' @@ -271,7 +268,6 @@ class Executor: elif self.data and self.node_data.body.type == "x-www-form-urlencoded": body = urlencode(self.data) elif self.data and self.node_data.body.type == "form-data": - boundary = self.boundary for key, value in self.data.items(): body += f"--{boundary}\r\n" body += f'Content-Disposition: form-data; name="{key}"\r\n\r\n' diff --git a/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py b/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py index 12c469a81a..7c19de6078 100644 --- a/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py +++ b/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py @@ -196,3 +196,72 @@ def test_extract_selectors_from_template_with_newline(): ) assert executor.params == {"test": "line1\nline2"} + + +def test_executor_with_form_data(): + # Prepare the variable pool + variable_pool = VariablePool( + system_variables={}, + user_inputs={}, + ) + variable_pool.add(["pre_node_id", "text_field"], "Hello, World!") + variable_pool.add(["pre_node_id", "number_field"], 42) + + # Prepare the node data + node_data = HttpRequestNodeData( + title="Test Form Data", + method="post", + url="https://api.example.com/upload", + authorization=HttpRequestNodeAuthorization(type="no-auth"), + headers="Content-Type: multipart/form-data", + params="", + body=HttpRequestNodeBody( + type="form-data", + data=[ + BodyData( + key="text_field", + type="text", + value="{{#pre_node_id.text_field#}}", + ), + BodyData( + key="number_field", + type="text", + value="{{#pre_node_id.number_field#}}", + ), + ], + ), + ) + + # Initialize the Executor + executor = Executor( + node_data=node_data, + timeout=HttpRequestNodeTimeout(connect=10, read=30, write=30), + variable_pool=variable_pool, + ) + + # Check the executor's data + assert executor.method == "post" + assert executor.url == "https://api.example.com/upload" + assert "Content-Type" in executor.headers + assert "multipart/form-data" in executor.headers["Content-Type"] + assert executor.params == {} + assert executor.json is None + assert executor.files is None + assert executor.content is None + + # Check that the form data is correctly loaded in executor.data + assert isinstance(executor.data, dict) + assert "text_field" in executor.data + assert executor.data["text_field"] == "Hello, World!" + assert "number_field" in executor.data + assert executor.data["number_field"] == "42" + + # Check the raw request (to_log method) + raw_request = executor.to_log() + assert "POST /upload HTTP/1.1" in raw_request + assert "Host: api.example.com" in raw_request + assert "Content-Type: multipart/form-data" in raw_request + assert "text_field" in raw_request + assert "Hello, World!" in raw_request + assert "number_field" in raw_request + assert "42" in raw_request