CY350

On this page

  • HTTP Server
    • Resources
    • Starter Code
    • Testing
    • Walkthrough

In-Class Lab 3 (10 points)

Initial setup of your development environment using WSL and VSCode. This lab introduces you to basic Linux commands, Python execution, and packet capturing.

Basic Client-Server Communication with HTTP and netcat
Published

February 4, 2026

Upon the completion of all activities, have your instructor review your work and track your progress to receive full credit.

HTTP Server

Copy the following code into a file named http_server.py on your VM or WSL. Try the copy icon on the top-right of the code block. Fill in the blanks. Run it to start listening for HTTP requests on port 8080. You can stop the server with Ctrl+C.

Resources

  • HTTP Message Format - Mozilla docs
  • socket library - Python docs -> see Echo Server example

Starter Code

http_server.py
import socket
import time


class TCPServer:
    def __init__(self, server_ip='127.0.0.1', server_port=8080):
        print("Initializing TCP Server...")
        self.server_ip = server_ip
        self.server_port = server_port
        self.listening = False
        self.setup_socket()

        self.resources = {
            "/": "Welcome to the TCP Server!",
            "/hello": "Hello, TCP Client!",
            "/goodbye": "Goodbye from the TCP Server!",
            "/joke": "Why do programmers prefer dark mode? Because light attracts bugs!"
        }

    def setup_socket(self) -> None:
        try:
            print(f"Creating TCP socket")

            # TODO create a TCP listening socket
            # TODO bind the socket
            # TODO start listening

            self.listening = True
            # print(...)

        except Exception as e:
            print(f"Error setting up socket: {e}")
            self.listening = False

        print('NOT IMPLEMENTED')

    def acceptConnection(self) -> None:

        print('Accepting connections...')

        # TODO accept a connection
        # print(...)

        # TODO store the request data somewhere
        # TODO in a loop, receive data until the full request is obtained

        # TODO use the connection to receive data
        # TODO append the received data somewhere

        # print(...)
        # TODO once the request is received, parse it

        # TODO build the response using build_http_response()
        # print(...)
        # TODO send the response using send_response()
        # print(...)
        # TODO close the connection
        # print(...)

    def parse_request(self, request: str) -> tuple[str | None, str | None]:
        method = None
        resource = None

        # TODO parse the request string and extract the method and resource

        return method, resource

    def build_http_response(self, method: str, resource: str) -> str:
        responseCode = 500
        responseMsg = "Internal Server Error"
        responseBody = "Sadness ensues. Something did not go as planned. All hope is lost."

        # TODO parse the resource and create the corresponding message
        # TODO 200 OK
        # TODO 404 Not Found
        # TODO 400 Bad Request

        http_response = None
        return http_response

    def send_response(self, response) -> None:
        print('NOT IMPLEMENTED')
        # TODO send the response through the connection

    def close_connection(self) -> None:
        self.sock.close()
        print("Listening socket closed.")


if __name__ == "__main__":
    server = TCPServer()
    server.listening = True
    try:
        while server.listening:
            server.acceptConnection()
            time.sleep(1)
    except KeyboardInterrupt:
        print("Shutting down server...")
        server.listening = False
    finally:
        server.close_connection()

Testing

  • Use curl to test your server: curl http://localhost:8080/hello
    • You should see the message “Hello, TCP Client!” in the terminal.
  • Use curl -v to see the full HTTP response.
  • Try other resources like /, /goodbye, and /joke.
  • Try an invalid resource like /invalid to see the 404 response.

Walkthrough

Create a listening socket and assign it to a class attribute.

# create a TCP listening socket
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# bind the socket
HOST = self.server_ip
PORT = self.server_port
self.sock.bind((HOST, PORT))

# start listening
self.sock.listen(1)
print(f"Listening on {self.server_ip}:{self.server_port}")

Accept incoming connections in a loop and handle them.

print('Ready to accept a connection...')

# accept a connection
conn, addr = self.sock.accept()
print(f'Accepted connection from {addr}')

self.conn = conn

# store the request data somewhere
received = b''
data = b''

# in a loop, receive data until the full request is obtained
while not data.endswith(b'\r\n\r\n'):
    # use the connection to receive data
    data = conn.recv(1024)
    # append the received data somewhere
    received += data

print(f'Received data: {len(received)} bytes')

receivedStr = received.decode()

print(f'======= REQUEST =======\n{receivedStr}\n=======================')

# once the request is received, parse it
method, resource = self.parse_request(receivedStr)

print(f'Method: {method}, Resource: {resource}')

# build the response using build_http_response()
response = self.build_http_response(method, resource)

print(f'======= RESPONSE =======\n{response}\n=======================')

# send the response using send_response()
# print(...)
self.send_response(response)

# close the connection
self.conn.close()
self.conn = None

print(f'Connection closed: {addr}')
# print(...)

Parse the HTTP request to extract the method and resource.

items = request.split()
method = items[0]
resource = items[1]

Build the HTTP response based on the method and resource.

if method != "GET":
    responseCode = 400
    responseMsg = "BAD REQUEST"
    responseBody = "Only GET method is supported."

elif not resource in self.resources:
    responseCode = 404
    responseMsg = "NOT FOUND"

else:
    responseCode = 200
    responseMsg = "OK"
    responseBody = self.resources[resource]

responseBody += '\r\n'
header = f'Content-Length: {len(responseBody)}'

http_response = f'HTTP/1.1 {responseCode} {responseMsg}\r\n{header}\r\n\r\n{responseBody}'

Send the HTTP response back to the client.

bytes = response.encode()
self.conn.sendall(bytes)
print(f'Sent: {len(bytes)} bytes')

Finally, close the connection.

def close_connection(self) -> None:
    if self.conn:
        self.conn.close()
    self.sock.close()
    print("Listening socket closed.")

Reuse

CC BY-NC-SA 4.0
 

© 2026 United States Military Academy