3 min read

Simple HTTP Proxy

This guide demonstrates how to build a basic multi-threaded HTTP proxy server using Python's socket module. This proxy intercepts HTTP requests, forwards them to the destination server, and relays the response back to the client.

Modules Used:

  • socket: To create network connections.
  • threading: To handle multiple client connections simultaneously.
  • argparse: To handle command-line arguments.

The Code

Save this as proxy.py.

import socket
import threading
import argparse
import sys

def handle_client(client_socket):
    try:
        # Receive request from client
        request = client_socket.recv(4096)
        if not request:
            client_socket.close()
            return

        # Parse the first line to extract the URL
        # Request line format: METHOD URL VERSION
        first_line = request.split(b'\n')[0]
        url = first_line.split(b' ')[1]

        # Parse URL to find the destination host and port
        http_pos = url.find(b"://")
        if http_pos == -1:
            temp = url
        else:
            temp = url[(http_pos+3):]

        port_pos = temp.find(b":")
        webserver_pos = temp.find(b"/")
        if webserver_pos == -1:
            webserver_pos = len(temp)

        webserver = b""
        port = -1

        if port_pos == -1 or webserver_pos < port_pos:
            port = 80
            webserver = temp[:webserver_pos]
        else:
            # Extract port if specified
            port = int((temp[(port_pos+1):])[:webserver_pos-port_pos-1])
            webserver = temp[:port_pos]

        print(f"Forwarding request to {webserver.decode()}:{port}")
        proxy_server(webserver.decode(), port, client_socket, request)

    except Exception as e:
        client_socket.close()

def proxy_server(webserver, port, client_socket, request_data):
    try:
        # Connect to the destination server
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(10)
        s.connect((webserver, port))
        s.send(request_data)

        while True:
            # Receive data from web server
            data = s.recv(4096)
            if len(data) > 0:
                # Send data back to client
                client_socket.send(data)
            else:
                break
        s.close()
        client_socket.close()
    except Exception as e:
        if 's' in locals(): s.close()
        client_socket.close()

def start_proxy(port):
    try:
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind(('0.0.0.0', port))
        server.listen(50)
        print(f"Proxy server listening on port {port}...")

        while True:
            client_sock, addr = server.accept()
            client_handler = threading.Thread(target=handle_client, args=(client_sock,))
            client_handler.daemon = True
            client_handler.start()

    except Exception as e:
        print(f"Server error: {e}")
        sys.exit(1)
    except KeyboardInterrupt:
        print("\nShutting down proxy.")
        sys.exit(0)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Simple HTTP Proxy")
    parser.add_argument("--port", type=int, default=8888, help="Port to listen on (default: 8888)")

    args = parser.parse_args()

    start_proxy(args.port)

Usage

  1. Run the proxy:

    python proxy.py --port 8888
  2. Configure your browser or tool:

    • Set your browser's HTTP proxy settings to localhost (or 127.0.0.1) and port 8888.
    • Or test with curl:
      curl -x http://localhost:8888 http://httpbin.org/ip

Note: This simple proxy handles standard HTTP requests. It does not implement the CONNECT method required for HTTPS tunneling, so it will likely fail with https:// URLs unless extended.

programming/python/python