From fa6d2874b302e77564b151e36409961a9dc6b0c9 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 ++--- .../workflow/nodes/test_http_request_node.py | 94 +++++++++++++++++++ 2 files changed, 103 insertions(+), 13 deletions(-) diff --git a/api/core/workflow/nodes/http_request/executor.py b/api/core/workflow/nodes/http_request/executor.py index 6872478299..d194f2a105 100644 --- a/api/core/workflow/nodes/http_request/executor.py +++ b/api/core/workflow/nodes/http_request/executor.py @@ -89,15 +89,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: @@ -146,9 +137,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) @@ -209,6 +199,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 @@ -236,6 +227,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" @@ -248,7 +246,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' @@ -263,7 +260,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/test_http_request_node.py b/api/tests/unit_tests/core/workflow/nodes/test_http_request_node.py index 720037d05f..0db566f289 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_http_request_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_http_request_node.py @@ -367,3 +367,97 @@ def test_executor_with_json_body_and_nested_object_variable(): assert '"name": "John Doe"' in raw_request assert '"age": 30' in raw_request assert '"email": "john@example.com"' in raw_request + + +def test_extract_selectors_from_template_with_newline(): + variable_pool = VariablePool() + variable_pool.add(("node_id", "custom_query"), "line1\nline2") + node_data = HttpRequestNodeData( + title="Test JSON Body with Nested Object Variable", + method="post", + url="https://api.example.com/data", + authorization=HttpRequestNodeAuthorization(type="no-auth"), + headers="Content-Type: application/json", + params="test: {{#node_id.custom_query#}}", + body=HttpRequestNodeBody( + type="none", + data=[], + ), + ) + + executor = Executor( + node_data=node_data, + timeout=HttpRequestNodeTimeout(connect=10, read=30, write=30), + variable_pool=variable_pool, + ) + + 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