mirror of https://github.com/langgenius/dify.git
fix(http_request): send form data (#10431)
This commit is contained in:
parent
a60133bfb3
commit
438ad8148b
|
@ -97,15 +97,6 @@ class Executor:
|
||||||
headers = self.variable_pool.convert_template(self.node_data.headers).text
|
headers = self.variable_pool.convert_template(self.node_data.headers).text
|
||||||
self.headers = _plain_text_to_dict(headers)
|
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):
|
def _init_body(self):
|
||||||
body = self.node_data.body
|
body = self.node_data.body
|
||||||
if body is not None:
|
if body is not None:
|
||||||
|
@ -154,9 +145,8 @@ class Executor:
|
||||||
for k, v in files.items()
|
for k, v in files.items()
|
||||||
if v.related_id is not None
|
if v.related_id is not None
|
||||||
}
|
}
|
||||||
|
|
||||||
self.data = form_data
|
self.data = form_data
|
||||||
self.files = files
|
self.files = files or None
|
||||||
|
|
||||||
def _assembling_headers(self) -> dict[str, Any]:
|
def _assembling_headers(self) -> dict[str, Any]:
|
||||||
authorization = deepcopy(self.auth)
|
authorization = deepcopy(self.auth)
|
||||||
|
@ -217,6 +207,7 @@ class Executor:
|
||||||
"timeout": (self.timeout.connect, self.timeout.read, self.timeout.write),
|
"timeout": (self.timeout.connect, self.timeout.read, self.timeout.write),
|
||||||
"follow_redirects": True,
|
"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)
|
response = getattr(ssrf_proxy, self.method)(**request_args)
|
||||||
return response
|
return response
|
||||||
|
@ -244,6 +235,13 @@ class Executor:
|
||||||
raw += f"Host: {url_parts.netloc}\r\n"
|
raw += f"Host: {url_parts.netloc}\r\n"
|
||||||
|
|
||||||
headers = self._assembling_headers()
|
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():
|
for k, v in headers.items():
|
||||||
if self.auth.type == "api-key":
|
if self.auth.type == "api-key":
|
||||||
authorization_header = "Authorization"
|
authorization_header = "Authorization"
|
||||||
|
@ -256,7 +254,6 @@ class Executor:
|
||||||
|
|
||||||
body = ""
|
body = ""
|
||||||
if self.files:
|
if self.files:
|
||||||
boundary = self.boundary
|
|
||||||
for k, v in self.files.items():
|
for k, v in self.files.items():
|
||||||
body += f"--{boundary}\r\n"
|
body += f"--{boundary}\r\n"
|
||||||
body += f'Content-Disposition: form-data; name="{k}"\r\n\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":
|
elif self.data and self.node_data.body.type == "x-www-form-urlencoded":
|
||||||
body = urlencode(self.data)
|
body = urlencode(self.data)
|
||||||
elif self.data and self.node_data.body.type == "form-data":
|
elif self.data and self.node_data.body.type == "form-data":
|
||||||
boundary = self.boundary
|
|
||||||
for key, value in self.data.items():
|
for key, value in self.data.items():
|
||||||
body += f"--{boundary}\r\n"
|
body += f"--{boundary}\r\n"
|
||||||
body += f'Content-Disposition: form-data; name="{key}"\r\n\r\n'
|
body += f'Content-Disposition: form-data; name="{key}"\r\n\r\n'
|
||||||
|
|
|
@ -196,3 +196,72 @@ def test_extract_selectors_from_template_with_newline():
|
||||||
)
|
)
|
||||||
|
|
||||||
assert executor.params == {"test": "line1\nline2"}
|
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
|
||||||
|
|
Loading…
Reference in New Issue