pythonsocketsservertcppersistent-connection

Persistent TCP Connections in Python


I'm trying to build a simple HTTP web server that serves a single file (index.html) over a persistent TCP connection. Specifically, I want the browser to be able to send multiple HTTP requests over the same TCP socket (only one socket at a time). To implement this, I've written the following code:

from socket import *
import sys

serverPort = int(sys.argv[1])
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(("localhost", serverPort))
serverSocket.listen(1)
print("Listening on port " + str(serverPort) + "...")
while True:
    connectionSocket, addr = serverSocket.accept()
    while True:
        request = connectionSocket.recv(4096)
        if not request:
            print("BROKEN")
            break
        request = request.decode()
        headers = request.split("\n")
        requestedFile = headers[0].split(" ")[1]
        if (requestedFile[0] == "/"):
            requestedFile = requestedFile[1:]
        fileInput = open(requestedFile)
        content = fileInput.read()
        fileInput.close()
        response = "HTTP/1.1 200 OK\r\n" + "Connection: Keep-Alive\r\n\r\n" + content
        connectionSocket.send(response.encode())
    connectionSocket.close()

There are two loops. The outer loop creates the socket, and also closes it once the inner loop terminates. The inner loop receives requests and generates responses. When I run the server on port 1000 and connect in my browser (Microsoft Edge) at 'http://localhost:1000/index.html' the HTML page is displayed properly, and my browser's network tab doesn't indicate any activity. But the loading circle on the tab continues spinning forever, making it seem like something hasn't loaded properly. What might be causing this?

Thank you!


Solution

  • This happens because the browser does not know how long the content body is. You can either provide a Content-Length header in your response or you can close the connection to let the browser know that the complete response has been sent.

    from socket import *
    import sys
    
    serverPort = int(sys.argv[1])
    serverSocket = socket(AF_INET, SOCK_STREAM)
    serverSocket.bind(("localhost", serverPort))
    serverSocket.listen(1)
    print("Listening on port " + str(serverPort) + "...")
    
    while True:
        # Create socket
        connectionSocket, addr = serverSocket.accept()
        
        while True:
            # Get request from socket
            request = connectionSocket.recv(4096)
            if not request:
                print("BROKEN")
                break
    
            # Parse request to determine requested file
            request = request.decode()
            headers = request.split("\n")
            requestedFile = headers[0].split(" ")[1]
            if (requestedFile[0] == "/"):
                requestedFile = requestedFile[1:]
            
            # Read requested file and generate/send response
            fileInput = open(requestedFile)
            content = fileInput.read()
            fileInput.close()
            response = "HTTP/1.1 200 OK\r\n" + "Connection: Keep-Alive\r\n" + "Content-Length: {}\r\n\r\n".format(len(content)) +content 
            connectionSocket.send(response.encode())
        connectionSocket.close()
    

    References: https://www.w3.org/Protocols/HTTP/1.0/draft-ietf-http-spec.html#BodyLength