Created
February 7, 2017 21:08
-
-
Save joncardasis/cc67cfb160fa61a0457d6951eff2aeae to your computer and use it in GitHub Desktop.
A simple and quick HTTP web server in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### 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.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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)) |
Doesn't Work
@Hebertm2 there are some things wrong with the code:
- Each HTTP header line should end with CRLF, like
"\r\n"
- The
client.send(response)
call doesn't guarantee that all data will be sent. You need to store the result ofsend()
which is the actual amount of bytes sent, and then send the rest if necessary.
Starting server on Mac-mini:8080
Error: Could not bind to port 8080
Shutting down server
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
WebServer.py line 45
s.socket.shutdown(socket.SHUT_RDWR)
typo: "s" -> "self"