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