不幸的是,我并没有真正了解您的代码应该如何工作,所以我在这里提出了我对简单 HTTP 代理应该是什么样子的想法。
那么一个基本的代理服务器应该做什么:
- 接受来自客户端的连接并接收 HTTP 请求。
- 解析请求并提取其目的地。
- 转发请求和响应。
- (可选)支持
Connection: keep-alive。
让我们一步一步写一些非常简化的代码。
代理如何接受客户端。应该创建一个套接字并将其移至被动模式:
import socket, select
sock = socket.socket()
sock.bind((your_ip, port))
sock.listen()
while True:
client_sock = sock.accept()
do_stuff(client_sock)
一旦建立 TCP 连接,是时候接收请求了。假设我们会得到这样的东西:
GET /?a=1&b=2 HTTP/1.1
Host: localhost
User-Agent: my browser details
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
在 TCP 中,不保留消息边界,因此我们应该等到至少获得前两行(对于 GET 请求)才能知道以后要做什么:
def do_stuff(sock):
data = receive_two_lines(sock)
remote_host = parse_request(data)
获得远程主机名后,就可以转发请求和响应了:
def do_stuff(client_sock):
data = receive_two_lines(client_sock)
remote_host = parse_request(data)
remote_ip = socket.getaddrinfo(remote_host) # see the docs for exact use
webserver = socket.socket()
webserver.connect((remote_ip, 80))
webserver.sendall(data)
while it_makes_sense():
client_ready = select.select([client_sock], [], [])[0]
web_ready = select.select([webserver], [], [])[0]
if client_ready:
webserver.sendall(client_sock.recv(1024))
if web_ready:
client_sock.sendall(webserver.recv(1024))
请注意select - 这是我们知道远程对等方是否向我们发送数据的方式。我还没有运行和测试这段代码,还有一些事情要做:
- 很有可能,您将在单个
client_sock.recv(1024) 调用中收到多个GET 请求,因为TCP 中没有保留消息边界。每次收到数据时,可能会查看额外的 get 请求。
- 对于 POST、HEAD、PUT、DELETE 和其他类型的请求,请求可能不同。相应地解析它们。
- 浏览器和服务器通常通过在标头中设置
Connection: keep-alive 选项来使用一个TCP 连接,但它们也可能决定放弃它。准备好检测远程对等方关闭的断开连接和套接字(为简单起见,在代码中称为while it_makes_sense())。
-
bind、listen、accept、recv、send、sendall、getaddrinfo、select - 所有这些函数都可以抛出异常。最好抓住它们并采取相应的行动。
- 代码当前一次为一个客户端提供服务。