initial
This commit is contained in:
8
README.md
Normal file
8
README.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# serve
|
||||||
|
|
||||||
|
My Python port of the webserver Gary Bernhardt built in [this
|
||||||
|
screencast](https://www.destroyallsoftware.com/screencasts/catalog/http-server-from-scratch).
|
||||||
|
|
||||||
|
It is not spec-compliant, serves files from disk, is vulnerable to directory
|
||||||
|
traversal, and if those files are executable, executes them. It’s as terrible
|
||||||
|
as it sounds!
|
107
serve.py
Normal file
107
serve.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
class Connection():
|
||||||
|
def __init__(self, sock):
|
||||||
|
self.sock = sock
|
||||||
|
self.buffer = ""
|
||||||
|
|
||||||
|
def read_until(self, sep):
|
||||||
|
while sep not in self.buffer:
|
||||||
|
self.buffer += self.sock.recv(4096)
|
||||||
|
res, self.buffer = self.buffer.split(sep, 1)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def read_line(self):
|
||||||
|
return self.read_until("\r\n")
|
||||||
|
|
||||||
|
def read_request(self):
|
||||||
|
request_line = self.read_line()
|
||||||
|
method, path, version = request_line.split(" ", 3)
|
||||||
|
|
||||||
|
headers = {}
|
||||||
|
|
||||||
|
line = self.read_line()
|
||||||
|
while len(line):
|
||||||
|
k, v = line.split(":", 1)
|
||||||
|
headers[k] = v.strip()
|
||||||
|
line = self.read_line()
|
||||||
|
|
||||||
|
# we do not read the body
|
||||||
|
|
||||||
|
return Request(method, path, headers)
|
||||||
|
|
||||||
|
|
||||||
|
class Request():
|
||||||
|
def __init__(self, method, path, headers):
|
||||||
|
self.method = method
|
||||||
|
self.path = path
|
||||||
|
self.headers = headers
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Request(method='{}' path='{}' headers='{}')".format(self.method,
|
||||||
|
self.path,
|
||||||
|
self.headers)
|
||||||
|
|
||||||
|
|
||||||
|
MSGS = {
|
||||||
|
200: "OK",
|
||||||
|
400: "Bad Request",
|
||||||
|
401: "Unauthorized",
|
||||||
|
403: "Forbidden",
|
||||||
|
404: "Not Found",
|
||||||
|
500: "Internal Server Error",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def respond(conn, status_code, content):
|
||||||
|
conn.send("HTTP/1.1 {} {}\r\n".format(status_code, MSGS[status_code]), 0)
|
||||||
|
conn.send("Content-Length: {}\r\n".format(len(content)))
|
||||||
|
conn.send("\r\n", 0)
|
||||||
|
conn.send(content, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def respond_to(conn, req):
|
||||||
|
content = ""
|
||||||
|
try:
|
||||||
|
path = os.getcwd() + req.path
|
||||||
|
if os.path.exists(path):
|
||||||
|
if os.access(path, os.X_OK):
|
||||||
|
content = subprocess.check_output([path])
|
||||||
|
else:
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
status_code = 200
|
||||||
|
else:
|
||||||
|
status_code = 404
|
||||||
|
except Exception as e:
|
||||||
|
content = str(e)
|
||||||
|
status_code = 500
|
||||||
|
respond(conn, status_code, content)
|
||||||
|
|
||||||
|
|
||||||
|
def accept(s):
|
||||||
|
conn, addr_info = s.accept()
|
||||||
|
connection = Connection(conn)
|
||||||
|
req = connection.read_request()
|
||||||
|
respond_to(conn, req)
|
||||||
|
|
||||||
|
|
||||||
|
def accept_loop(s):
|
||||||
|
while True:
|
||||||
|
accept(s)
|
||||||
|
|
||||||
|
|
||||||
|
def listen():
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
|
||||||
|
s.bind(('', 8080))
|
||||||
|
s.listen(5)
|
||||||
|
|
||||||
|
accept_loop(s)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
listen()
|
Reference in New Issue
Block a user