使用Socket发起HTTP请求时遭遇400 Bad Request问题求助
解决Python手动构造HTTP GET请求返回400 Bad Request的问题
当运行你提供的代码并输入users.uop.gr/~kyianno/这类链接时,会收到400错误,核心问题出在HTTP协议细节处理、状态码解析逻辑以及代码边界情况处理上,以下是具体问题和修正方案:
原代码的核心问题
- HTTP请求换行符不符合规范:HTTP 1.1协议要求请求头的每行结尾必须是
\r\n(回车+换行),原代码使用单个\n,服务器无法正确解析请求格式,直接返回400错误。 - 状态码解析逻辑完全错误:原代码尝试用
struct.unpack("!H", response[:2])[0]解析状态码,但HTTP响应开头是HTTP/1.1 200 OK\r\n这类文本格式,前两个字节是HT,转成整数完全不是状态码,导致后续逻辑无法触发。 - 文件路径定义存在漏洞:仅在目录不存在时定义
file_path,若目录已存在,后续执行open(file_path)会抛出NameError。 - 请求路径边界处理缺失:若输入纯域名(如
users.uop.gr),拆分后object_name为空,此时GET请求会变成GET HTTP/1.1(路径为空),属于无效请求。
修正后的代码
import socket import os def parse_hyperlink(hyperlink): # 处理路径为空的情况,默认设为/ parts = hyperlink.split("/") hostname = parts[0] if len(parts) > 1: object_name = "/" + "/".join(parts[1:]) else: object_name = "/" # 避免末尾多斜杠的问题(可选,不影响但更规范) if object_name == "//": object_name = "/" return hostname, object_name def get_host_address(hostname): host_address = socket.gethostbyname(hostname) return host_address def main(): hyperlink = input("Enter a hyperlink: ") hostname, object_name = parse_hyperlink(hyperlink) host_address = get_host_address(hostname) server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.connect((host_address, 80)) # 使用HTTP规范的\r\n换行符 request = f"GET {object_name} HTTP/1.1\r\n" request += f"Host: {hostname}\r\n" request += "Connection: close\r\n" # 显式关闭连接,方便接收完整响应 request += "\r\n" # 空行表示请求头结束 server_socket.sendall(request.encode("utf-8")) response = b"" while True: data = server_socket.recv(1024) # 增大缓冲区,提高接收效率 if not data: break response += data # 正确解析状态码:拆分响应头文本 response_str = response.decode("utf-8") status_line = response_str.split("\r\n")[0] status_code = int(status_line.split()[1]) if status_code == 200: desktop_path = os.path.expanduser("~/Desktop") directory = os.path.join(desktop_path, "my_directory") os.makedirs(directory, exist_ok=True) # 直接创建,已存在则忽略 file_path = os.path.join(directory, "response.html") # 分离响应头和响应体,只保存响应体(可选,若要完整响应可去掉切片) body_start = response_str.find("\r\n\r\n") + 4 with open(file_path, "wb") as f: f.write(response[body_start:]) print(response_str) server_socket.close() if __name__ == "__main__": main()
关键修改说明
- 修正HTTP换行符:所有请求头换行使用
\r\n,符合HTTP 1.1协议规范,解决400错误的核心原因。 - 正确解析状态码:从响应的第一行文本中提取状态码,逻辑符合HTTP响应格式。
- 修复文件路径逻辑:使用
os.makedirs(directory, exist_ok=True)确保目录存在,无论目录是否已创建都定义file_path。 - 完善路径处理:当输入纯域名时,自动将请求路径设为
/,避免无效请求。 - 优化接收缓冲区:将
recv(255)改为recv(1024),提高大响应的接收效率;添加Connection: close头,确保服务器发送完响应后关闭连接,避免无限等待。
内容的提问来源于stack exchange,提问作者Τηλέμαχος Γκουλέτσας
相关产品推荐
相关产品推荐

