Skip to content

Instantly share code, notes, and snippets.

@joncardasis
Created February 7, 2017 21:08
Show Gist options
  • Save joncardasis/cc67cfb160fa61a0457d6951eff2aeae to your computer and use it in GitHub Desktop.
Save joncardasis/cc67cfb160fa61a0457d6951eff2aeae to your computer and use it in GitHub Desktop.
A simple and quick HTTP web server in Python
### MAIN ###
def shutdownServer(sig, unused):
"""
Shutsdown server from a SIGINT recieved signal
"""
server.shutdown()
sys.exit(1)
signal.signal(signal.SIGINT, shutdownServer)
server = WebServer(8080)
server.start()
print("Press Ctrl+C to shut down server.")
"""
Author: Jonathan Cardasis
"""
import socket
import signal # Allow socket destruction on Ctrl+C
import sys
import time
import threading
class WebServer(object):
"""
Class for describing simple HTTP server objects
"""
def __init__(self, port=8080):
self.host = socket.gethostname().split('.')[0] # Default to any avialable network interface
self.port = port
self.content_dir = 'web' # Directory where webpage files are stored
def start(self):
"""
Attempts to create and bind a socket to launch the server
"""
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
print("Starting server on {host}:{port}".format(host=self.host, port=self.port))
self.socket.bind((self.host, self.port))
print("Server started on port {port}.".format(port=self.port))
except Exception as e:
print("Error: Could not bind to port {port}".format(port=self.port))
self.shutdown()
sys.exit(1)
self._listen() # Start listening for connections
def shutdown(self):
"""
Shuts down the server
"""
try:
print("Shutting down server")
s.socket.shutdown(socket.SHUT_RDWR)
except Exception as e:
pass # Pass if socket is already closed
def _generate_headers(self, response_code):
"""
Generate HTTP response headers.
Parameters:
- response_code: HTTP response code to add to the header. 200 and 404 supported
Returns:
A formatted HTTP header for the given response_code
"""
header = ''
if response_code == 200:
header += 'HTTP/1.1 200 OK\n'
elif response_code == 404:
header += 'HTTP/1.1 404 Not Found\n'
time_now = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
header += 'Date: {now}\n'.format(now=time_now)
header += 'Server: Simple-Python-Server\n'
header += 'Connection: close\n\n' # Signal that connection will be closed after completing the request
return header
def _listen(self):
"""
Listens on self.port for any incoming connections
"""
self.socket.listen(5)
while True:
(client, address) = self.socket.accept()
client.settimeout(60)
print("Recieved connection from {addr}".format(addr=address))
threading.Thread(target=self._handle_client, args=(client, address)).start()
def _handle_client(self, client, address):
"""
Main loop for handling connecting clients and serving files from content_dir
Parameters:
- client: socket client from accept()
- address: socket address from accept()
"""
PACKET_SIZE = 1024
while True:
print("CLIENT",client)
data = client.recv(PACKET_SIZE).decode() # Recieve data packet from client and decode
if not data: break
request_method = data.split(' ')[0]
print("Method: {m}".format(m=request_method))
print("Request Body: {b}".format(b=data))
if request_method == "GET" or request_method == "HEAD":
# Ex) "GET /index.html" split on space
file_requested = data.split(' ')[1]
# If get has parameters ('?'), ignore them
file_requested = file_requested.split('?')[0]
if file_requested == "/":
file_requested = "/index.html"
filepath_to_serve = self.content_dir + file_requested
print("Serving web page [{fp}]".format(fp=filepath_to_serve))
# Load and Serve files content
try:
f = open(filepath_to_serve, 'rb')
if request_method == "GET": # Read only for GET
response_data = f.read()
f.close()
response_header = self._generate_headers(200)
except Exception as e:
print("File not found. Serving 404 page.")
response_header = self._generate_headers(404)
if request_method == "GET": # Temporary 404 Response Page
response_data = b"<html><body><center><h1>Error 404: File not found</h1></center><p>Head back to <a href="/">dry land</a>.</p></body></html>"
response = response_header.encode()
if request_method == "GET":
response += response_data
client.send(response)
client.close()
break
else:
print("Unknown HTTP request method: {method}".format(method=request_method))
@doko-desuka
Copy link

@Hebertm2 there are some things wrong with the code:

  1. Each HTTP header line should end with CRLF, like "\r\n"
  2. The client.send(response) call doesn't guarantee that all data will be sent. You need to store the result of send() which is the actual amount of bytes sent, and then send the rest if necessary.

@VosMottor
Copy link

Starting server on Mac-mini:8080
Error: Could not bind to port 8080
Shutting down server

@rokcarl
Copy link

rokcarl commented Apr 15, 2021

Could not bind to port 8080

This is because of the IP that the server binds to. Just change self.host to something like '127.0.0.1'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment