如何用 Python 搭建一个代理服务器

想做一个中转代理服务器,用 python 写,在浏览器上设置代理 ip : port ,然后把所有的 http 和 https 的包都进行转发,同时做一些不可描述的事情,如何搞呢?


如何用 Python 搭建一个代理服务器
13 回复

看看 SS 的源码不就知道了。。。


要搭一个简单的HTTP代理服务器,用Python的http.server模块和socketserver就能搞定。下面这个代码示例创建了一个基础的转发代理,能处理HTTP的GET和CONNECT请求(后者用于HTTPS隧道)。

import socket
import select
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingMixIn
import urllib.parse

class ProxyHTTPRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        # 解析客户端请求的URL
        url = urllib.parse.urlparse(self.path)
        if not url.netloc:
            # 如果路径是完整的URL(某些客户端直接发完整URL)
            full_url = urllib.parse.urlparse('http://' + self.path)
            host, port = full_url.netloc, 80
        else:
            host, port = url.netloc, 80
        
        # 提取端口号
        if ':' in host:
            host, port = host.split(':', 1)
            port = int(port)
        
        # 连接到目标服务器
        try:
            with socket.create_connection((host, port), timeout=10) as target_sock:
                # 构造HTTP请求行和头部
                request_line = f"GET {url.path or '/'} HTTP/1.1\r\n"
                headers = f"Host: {host}\r\n"
                # 转发客户端的其他头部(除了Hop-by-hop头部)
                for key, value in self.headers.items():
                    if key.lower() not in ['proxy-connection', 'connection', 'keep-alive']:
                        headers += f"{key}: {value}\r\n"
                headers += "\r\n"
                
                # 发送请求到目标服务器
                target_sock.sendall((request_line + headers).encode())
                
                # 接收响应并转发给客户端
                self._relay_data(target_sock, self.connection)
        except Exception as e:
            self.send_error(502, f"Proxy Error: {str(e)}")
    
    def do_CONNECT(self):
        # HTTPS隧道处理
        host_port = self.path.split(':', 1)
        if len(host_port) == 2:
            host, port = host_port[0], int(host_port[1])
        else:
            host, port = host_port[0], 443
        
        try:
            # 连接到目标服务器
            target_sock = socket.create_connection((host, port), timeout=10)
            
            # 告诉客户端连接已建立
            self.send_response(200, 'Connection Established')
            self.end_headers()
            
            # 双向转发数据
            sockets = [self.connection, target_sock]
            while True:
                readable, _, _ = select.select(sockets, [], [])
                for sock in readable:
                    data = sock.recv(8192)
                    if not data:
                        return
                    # 将数据转发到另一个socket
                    other = target_sock if sock is self.connection else self.connection
                    other.sendall(data)
        except Exception as e:
            self.send_error(502, f"Tunnel Error: {str(e)}")
        finally:
            if 'target_sock' in locals():
                target_sock.close()
    
    def _relay_data(self, source, destination):
        """在两个socket之间转发数据"""
        try:
            while True:
                data = source.recv(8192)
                if not data:
                    break
                destination.sendall(data)
        except:
            pass
    
    def log_message(self, format, *args):
        # 可选:自定义日志格式
        print(f"[Proxy] {self.address_string()} - {format % args}")

class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
    """多线程HTTP服务器"""
    daemon_threads = True

def run_proxy(host='localhost', port=8888):
    server = ThreadingHTTPServer((host, port), ProxyHTTPRequestHandler)
    print(f"代理服务器运行在 http://{host}:{port}")
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\n代理服务器已停止")
        server.server_close()

if __name__ == '__main__':
    run_proxy()

代码说明:

  1. ProxyHTTPRequestHandler 继承自 BaseHTTPRequestHandler,重写了 do_GETdo_CONNECT 方法来处理两种主要请求。
  2. do_GET 处理普通HTTP请求:解析URL,连接到目标服务器,转发请求和响应。
  3. do_CONNECT 处理HTTPS隧道(CONNECT方法):建立到目标服务器的TCP连接,然后在客户端和目标服务器之间双向转发原始数据。
  4. ThreadingHTTPServer 使用线程池处理并发连接。
  5. run_proxy() 启动服务器,默认监听本地8888端口。

使用方法:

  1. 保存代码为 proxy.py
  2. 运行 python proxy.py
  3. 在浏览器或应用中配置代理为 localhost:8888

这个代理支持基本的HTTP和HTTPS流量转发。如果需要更完整的功能(如认证、缓存、访问控制等),可以考虑使用现成的库如 mitmproxysquid

用现成的库更省事。

容易,一搜就有教程。

当年有个人也是这样想的,然后就照着网上的 socks5 教程做了个 ss

接楼上~后来那个人被叫去喝茶了

故事继续发展。。 c 大去了美国

你确定是肉翻了吗?

不清楚是不是肉番,推特上看到有人说的

看你的描述 你应该是从零开始的

不过我推荐你最好不要开始

人生多美好啊 何必去浪费时间

嗯好

-,-
发展成故事会了?
非常感谢

2333

肉翻是最好的方法

我们一开始用 Python 开发消息系统,后来改用 golang 。
其实现在很多 tunnel 都是用 golang 开发的,如果愿意学习,推荐看看:
1. https://github.com/getqujing/qtunnel (几百行代码,单向代理隧道,合适你的需求 , TCP 协议都支持)
2. https://github.com/ooclab/es (我们写的,从 Python 发展而来, 4000 行左右代码,双向对称设计)

回到顶部