Building a Simple HTTP Proxy Server in Python


A proxy server acts as an intermediary between a client and a server. It forwards client requests to the target server and then returns the response to the client. HTTP proxy servers are particularly useful for filtering content, providing anonymity, and caching responses. In this article, we will walk through the process of building a simple HTTP proxy server using Python.

Setting Up the Python Environment

Before we begin, ensure that you have Python installed on your system. We will be using the socket and http.client libraries to create the proxy server. Additionally, we need to open ports to listen for incoming connections and forward those requests to the appropriate servers.

To begin, create a new Python file and import the necessary libraries:

python
import socket
import threading
import http.client
import urllib
The socket library is used to create a server that listens for incoming client requests, while http.client helps with sending requests to the target server.

Creating a Basic Socket Server

First, we need to create a server that listens for HTTP requests from the client. This is done using the socket module to bind a server to a specific IP address and port.
python
def start_proxy_server(host, port):
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((host, port))
server_socket.listen(5)
print(f”Listening for connections on {host}:{port}…”)
while True:
client_socket, client_address = server_socket.accept()
print(f”Connection established with {client_address}”)
handle_client_request(client_socket)
The start_proxy_server function sets up a TCP socket that listens on the provided host and port. Once a connection is established, it will pass the socket to the handle_client_request function for processing.

Handling Client Requests

Now, we need to define the handle_client_request function. This function reads data from the client, processes the HTTP request, and forwards it to the target server.
python
def handle_client_request(client_socket):
request = client_socket.recv(4096)
if not request:
client_socket.close()
return

# Parse the request
request_line = request.splitlines()[0]
method, url, _ = request_line.decode().split(” “)

print(f”Request method: {method}, URL: {url}”)

# Forward the request to the target server
url_parts = urllib.parse.urlparse(url)
server = url_parts.netloc
path = url_parts.path if url_parts.path else ‘/’
headers = {‘Host’: server}

# Establish connection to the server
conn = http.client.HTTPConnection(server)
conn.request(method, path, headers=headers)
response = conn.getresponse()

# Return the response to the client
client_socket.sendall(response.read())
client_socket.close()
This function begins by receiving the HTTP request from the client and parsing it to determine the request method and URL. It then forwards the request to the target server using http.client.HTTPConnection. The response from the server is read and sent back to the client.

Starting the Proxy Server

Finally, we need to start the proxy server by calling the start_proxy_server function. This can be done as follows:
python
if __name__ == “__main__”:
HOST, PORT = “127.0.0.1”, 8080
start_proxy_server(HOST, PORT)
This block of code will start the server on 127.0.0.1 (localhost) at port 8080. You can change the host and port as needed.

Multi-Threading for Handling Multiple Clients

To handle multiple client requests simultaneously, we will use Python’s threading module. Each incoming client connection will be handled in a separate thread to ensure that the server can process multiple requests concurrently.
Modify the start_proxy_server function to use threads:
python
def start_proxy_server(host, port):
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((host, port))
server_socket.listen(5)
print(f”Listening for connections on {host}:{port}…”)
while True:
client_socket, client_address = server_socket.accept()
print(f”Connection established with {client_address}”)
threading.Thread(target=handle_client_request, args=(client_socket,)).start()
By wrapping the handle_client_request function in a thread, the server can now manage multiple requests without blocking.

Conclusion

At this point, you have a fully functional, simple HTTP proxy server in Python. This server listens for client requests, forwards them to the appropriate target server, and returns the server’s response.
While this server is basic, it serves as a foundation for more advanced features such as caching, logging, and encryption. With additional enhancements, this proxy server can be adapted for various use cases such as content filtering, web scraping, or anonymous browsing.

We earn commissions using affiliate links.


14 Privacy Tools You Should Have

Learn how to stay safe online in this free 34-page eBook.


Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top