mirror of
https://github.com/langgenius/dify.git
synced 2026-04-23 00:18:22 +08:00
limit http response
This commit is contained in:
parent
0b07c6914a
commit
d24cf9e56a
@ -11,19 +11,42 @@ import core.helper.ssrf_proxy as ssrf_proxy
|
|||||||
from core.workflow.nodes.http_request.entities import HttpRequestNodeData
|
from core.workflow.nodes.http_request.entities import HttpRequestNodeData
|
||||||
|
|
||||||
HTTP_REQUEST_DEFAULT_TIMEOUT = (10, 60)
|
HTTP_REQUEST_DEFAULT_TIMEOUT = (10, 60)
|
||||||
|
MAX_BINARY_SIZE = 1024 * 1024 * 10 # 10MB
|
||||||
|
READABLE_MAX_BINARY_SIZE = '10MB'
|
||||||
|
MAX_TEXT_SIZE = 1024 * 1024 // 10 # 0.1MB
|
||||||
|
READABLE_MAX_TEXT_SIZE = '0.1MB'
|
||||||
|
|
||||||
class HttpExecutorResponse:
|
class HttpExecutorResponse:
|
||||||
status_code: int
|
|
||||||
headers: dict[str, str]
|
headers: dict[str, str]
|
||||||
body: bytes
|
response: Union[httpx.Response, requests.Response]
|
||||||
|
|
||||||
def __init__(self, status_code: int, headers: dict[str, str], body: bytes):
|
def __init__(self, response: Union[httpx.Response, requests.Response] = None):
|
||||||
"""
|
"""
|
||||||
init
|
init
|
||||||
"""
|
"""
|
||||||
self.status_code = status_code
|
headers = {}
|
||||||
|
if isinstance(response, httpx.Response):
|
||||||
|
for k, v in response.headers.items():
|
||||||
|
headers[k] = v
|
||||||
|
elif isinstance(response, requests.Response):
|
||||||
|
for k, v in response.headers.items():
|
||||||
|
headers[k] = v
|
||||||
|
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
self.body = body
|
self.response = response
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_file(self) -> bool:
|
||||||
|
"""
|
||||||
|
check if response is file
|
||||||
|
"""
|
||||||
|
content_type = self.get_content_type()
|
||||||
|
file_content_types = ['image', 'audio', 'video']
|
||||||
|
for v in file_content_types:
|
||||||
|
if v in content_type:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def get_content_type(self) -> str:
|
def get_content_type(self) -> str:
|
||||||
"""
|
"""
|
||||||
@ -32,17 +55,15 @@ class HttpExecutorResponse:
|
|||||||
for key, val in self.headers.items():
|
for key, val in self.headers.items():
|
||||||
if key.lower() == 'content-type':
|
if key.lower() == 'content-type':
|
||||||
return val
|
return val
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def extract_file(self) -> tuple[str, bytes]:
|
def extract_file(self) -> tuple[str, bytes]:
|
||||||
"""
|
"""
|
||||||
extract file from response if content type is file related
|
extract file from response if content type is file related
|
||||||
"""
|
"""
|
||||||
content_type = self.get_content_type()
|
if self.is_file:
|
||||||
file_content_types = ['image', 'audio', 'video']
|
return self.get_content_type(), self.body
|
||||||
for v in file_content_types:
|
|
||||||
if v in content_type:
|
|
||||||
return content_type, self.body
|
|
||||||
|
|
||||||
return '', b''
|
return '', b''
|
||||||
|
|
||||||
@ -51,7 +72,55 @@ class HttpExecutorResponse:
|
|||||||
"""
|
"""
|
||||||
get content
|
get content
|
||||||
"""
|
"""
|
||||||
return self.body.decode('utf-8')
|
if isinstance(self.response, httpx.Response):
|
||||||
|
return self.response.text
|
||||||
|
elif isinstance(self.response, requests.Response):
|
||||||
|
return self.response.text
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Invalid response type {type(self.response)}')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def body(self) -> bytes:
|
||||||
|
"""
|
||||||
|
get body
|
||||||
|
"""
|
||||||
|
if isinstance(self.response, httpx.Response):
|
||||||
|
return self.response.content
|
||||||
|
elif isinstance(self.response, requests.Response):
|
||||||
|
return self.response.content
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Invalid response type {type(self.response)}')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def status_code(self) -> int:
|
||||||
|
"""
|
||||||
|
get status code
|
||||||
|
"""
|
||||||
|
if isinstance(self.response, httpx.Response):
|
||||||
|
return self.response.status_code
|
||||||
|
elif isinstance(self.response, requests.Response):
|
||||||
|
return self.response.status_code
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Invalid response type {type(self.response)}')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def size(self) -> int:
|
||||||
|
"""
|
||||||
|
get size
|
||||||
|
"""
|
||||||
|
return len(self.body)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def readable_size(self) -> str:
|
||||||
|
"""
|
||||||
|
get readable size
|
||||||
|
"""
|
||||||
|
if self.size < 1024:
|
||||||
|
return f'{self.size} bytes'
|
||||||
|
elif self.size < 1024 * 1024:
|
||||||
|
return f'{(self.size / 1024):.2f} KB'
|
||||||
|
else:
|
||||||
|
return f'{(self.size / 1024 / 1024):.2f} MB'
|
||||||
|
|
||||||
class HttpExecutor:
|
class HttpExecutor:
|
||||||
server_url: str
|
server_url: str
|
||||||
@ -214,23 +283,20 @@ class HttpExecutor:
|
|||||||
"""
|
"""
|
||||||
validate the response
|
validate the response
|
||||||
"""
|
"""
|
||||||
if isinstance(response, httpx.Response):
|
if isinstance(response, httpx.Response | requests.Response):
|
||||||
# get key-value pairs headers
|
executor_response = HttpExecutorResponse(response)
|
||||||
headers = {}
|
|
||||||
for k, v in response.headers.items():
|
|
||||||
headers[k] = v
|
|
||||||
|
|
||||||
return HttpExecutorResponse(response.status_code, headers, response.content)
|
|
||||||
elif isinstance(response, requests.Response):
|
|
||||||
# get key-value pairs headers
|
|
||||||
headers = {}
|
|
||||||
for k, v in response.headers.items():
|
|
||||||
headers[k] = v
|
|
||||||
|
|
||||||
return HttpExecutorResponse(response.status_code, headers, response.content)
|
|
||||||
else:
|
else:
|
||||||
raise ValueError(f'Invalid response type {type(response)}')
|
raise ValueError(f'Invalid response type {type(response)}')
|
||||||
|
|
||||||
|
if executor_response.is_file:
|
||||||
|
if executor_response.size > MAX_BINARY_SIZE:
|
||||||
|
raise ValueError(f'File size is too large, max size is {READABLE_MAX_BINARY_SIZE}, but current size is {executor_response.readable_size}.')
|
||||||
|
else:
|
||||||
|
if executor_response.size > MAX_TEXT_SIZE:
|
||||||
|
raise ValueError(f'Text size is too large, max size is {READABLE_MAX_TEXT_SIZE}, but current size is {executor_response.readable_size}.')
|
||||||
|
|
||||||
|
return executor_response
|
||||||
|
|
||||||
def _do_http_request(self, headers: dict[str, Any]) -> httpx.Response:
|
def _do_http_request(self, headers: dict[str, Any]) -> httpx.Response:
|
||||||
"""
|
"""
|
||||||
do http request depending on api bundle
|
do http request depending on api bundle
|
||||||
|
|||||||
@ -11,7 +11,6 @@ from core.workflow.nodes.http_request.entities import HttpRequestNodeData
|
|||||||
from core.workflow.nodes.http_request.http_executor import HttpExecutor, HttpExecutorResponse
|
from core.workflow.nodes.http_request.http_executor import HttpExecutor, HttpExecutorResponse
|
||||||
from models.workflow import WorkflowNodeExecutionStatus
|
from models.workflow import WorkflowNodeExecutionStatus
|
||||||
|
|
||||||
|
|
||||||
class HttpRequestNode(BaseNode):
|
class HttpRequestNode(BaseNode):
|
||||||
_node_data_cls = HttpRequestNodeData
|
_node_data_cls = HttpRequestNodeData
|
||||||
node_type = NodeType.HTTP_REQUEST
|
node_type = NodeType.HTTP_REQUEST
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user