""" HTTP请求工具 提供通用的HTTP API调用功能 """ import httpx from typing import Dict, Any, Optional, List class HTTPClientConfig: """HTTP客户端配置""" DEFAULT_TIMEOUT = 30 # 默认超时(秒) MAX_RESPONSE_SIZE = 5 * 1024 * 1024 # 最大响应大小(5MB) MAX_REDIRECTS = 5 # 最大重定向次数 ALLOWED_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"] class HTTPClient: """ HTTP客户端工具 安全特性: - 只允许特定HTTP方法 - 响应大小限制 - 超时控制 - 请求/响应日志 """ def __init__(self): self.default_timeout = HTTPClientConfig.DEFAULT_TIMEOUT async def request( self, url: str, method: str = "GET", params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, json_data: Optional[Dict[str, Any]] = None, data: Optional[Any] = None, timeout: Optional[int] = None, allow_redirects: bool = True ) -> Dict[str, Any]: """ 发送HTTP请求 Args: url: 目标URL method: HTTP方法 params: 查询参数 headers: 请求头 json_data: JSON请求体 data: 原始请求体 timeout: 超时时间 allow_redirects: 是否允许重定向 Returns: 响应结果 """ # 安全检查:方法 method = method.upper() if method not in HTTPClientConfig.ALLOWED_METHODS: return { "success": False, "error": f"Method '{method}' not allowed. Allowed: {HTTPClientConfig.ALLOWED_METHODS}" } # 安全检查:协议 if not url.startswith(("http://", "https://")): return { "success": False, "error": "Only HTTP and HTTPS protocols are allowed" } timeout = timeout or self.default_timeout try: async with httpx.AsyncClient( timeout=timeout, max_redirects=HTTPClientConfig.MAX_REDIRECTS if allow_redirects else 0, follow_redirects=allow_redirects, ) as client: response = await client.request( method=method, url=url, params=params, headers=headers, json=json_data, content=data, ) # 检查响应大小 content_length = len(response.content) if content_length > HTTPClientConfig.MAX_RESPONSE_SIZE: return { "success": False, "error": f"Response too large: {content_length} bytes" } # 解析响应 content_type = response.headers.get("content-type", "") if "application/json" in content_type: try: return { "success": True, "status_code": response.status_code, "url": str(response.url), "headers": dict(response.headers), "json": response.json() } except: pass # 文本响应 return { "success": True, "status_code": response.status_code, "url": str(response.url), "headers": dict(response.headers), "text": response.text[:HTTPClientConfig.MAX_RESPONSE_SIZE] } except httpx.TimeoutException: return { "success": False, "error": f"Request timeout ({timeout}s)" } except httpx.InvalidURL: return { "success": False, "error": "Invalid URL" } except Exception as e: return { "success": False, "error": str(e) } async def get( self, url: str, params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout: Optional[int] = None ) -> Dict[str, Any]: """发送GET请求""" return await self.request(url, "GET", params, headers, timeout=timeout) async def post( self, url: str, json_data: Optional[Dict[str, Any]] = None, data: Optional[Any] = None, headers: Optional[Dict[str, str]] = None, timeout: Optional[int] = None ) -> Dict[str, Any]: """发送POST请求""" return await self.request(url, "POST", None, headers, json_data, data, timeout) async def put( self, url: str, json_data: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout: Optional[int] = None ) -> Dict[str, Any]: """发送PUT请求""" return await self.request(url, "PUT", None, headers, json_data, None, timeout) async def delete( self, url: str, headers: Optional[Dict[str, str]] = None, timeout: Optional[int] = None ) -> Dict[str, Any]: """发送DELETE请求""" return await self.request(url, "DELETE", None, headers, timeout=timeout) # 全局HTTP客户端 http_client = HTTPClient() # 便捷函数 async def http_request( url: str, method: str = "GET", params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, json_data: Optional[Dict[str, Any]] = None, timeout: Optional[int] = None ) -> Dict[str, Any]: """发送HTTP请求""" return await http_client.request(url, method, params, headers, json_data, None, timeout) async def http_get( url: str, params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout: Optional[int] = None ) -> Dict[str, Any]: """发送GET请求""" return await http_client.get(url, params, headers, timeout) async def http_post( url: str, json_data: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout: Optional[int] = None ) -> Dict[str, Any]: """发送POST请求""" return await http_client.post(url, json_data, None, headers, timeout) async def http_put( url: str, json_data: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout: Optional[int] = None ) -> Dict[str, Any]: """发送PUT请求""" return await http_client.put(url, json_data, headers, timeout) async def http_delete( url: str, headers: Optional[Dict[str, str]] = None, timeout: Optional[int] = None ) -> Dict[str, Any]: """发送DELETE请求""" return await http_client.delete(url, headers, timeout) # 工具定义 HTTP_REQUEST_TOOL = { "name": "http_request", "description": "Make HTTP requests to APIs. Supports GET, POST, PUT, DELETE methods with JSON data.", "parameters": { "type": "object", "properties": { "url": { "type": "string", "description": "The URL to request" }, "method": { "type": "string", "description": "HTTP method (GET, POST, PUT, DELETE, PATCH)", "default": "GET" }, "params": { "type": "object", "description": "Query parameters for GET requests" }, "headers": { "type": "object", "description": "Request headers" }, "json_data": { "type": "object", "description": "JSON body for POST/PUT requests" }, "timeout": { "type": "integer", "description": "Request timeout in seconds", "default": 30 } }, "required": ["url"] } }