read only http finished

This commit is contained in:
Matt Arnold 2025-09-07 18:27:10 -04:00
parent a4846211dc
commit 3b3f15a6ac

62
http.py
View File

@ -33,10 +33,10 @@ Fork = False
# i suspect this one might be ai generated.
# It's a subclass of dict with key access via the dot
# operator. Similar to how ruby does things
# I don't know why this isn't the default
# i've coded this dozens of times but i couldn't use
# client's code in a public project.
# Thus cut 'n' paste.
# Obviously some things still need standard dictionary access
# i found this code looked at it, tested it, before using it
#
CRLF = "\r\n"
LF = "\n"
@ -78,13 +78,12 @@ class AccessDict(dict):
# In a future part. I will go over how to make a more full featured
# implementation. This is just making us ssl ready and laying the ground work
class HttpRequest(AccessDict):
def __init__(
self, method="GET", path="/", headers={}, body="goodbye\r\n", *args, **kwargs
):
def __init__(self, method="GET", path="/", headers={}, *args, **kwargs):
super().__init__(*args, **kwargs)
self["method"] = method
self["headers"] = headers
self["body"] = StringIO(body)
if "body" in kwargs:
self["body"] = StringIO(kwargs["body"])
self["path"] = path
if "Host" not in self["headers"]:
self["headers"]["Host"] = "localhost"
@ -96,14 +95,14 @@ class HttpRequest(AccessDict):
buf = StringIO()
buf.write(f"{self.method} {self.path} HTTP/1.1")
for k, v in self["headers"].items():
buf.write(f"{k}: {v}\r\n")
buf.write(CRLF + CRLF)
buf.write(self["body"].getvalue() + "\r\n")
return buf.getvalue() + "\r\n"
buf.write(f"{k}: {v}" + CRLF)
buf.write(CRLF)
buf.write(self["body"].getvalue() + CRLF)
return buf.getvalue() + CRLF
class HttpResponse(AccessDict):
def __init__(self, status="404", headers={}, body="goodbye\r\n", *args, **kwargs):
def __init__(self, status="400", headers={}, body="goodbye\r\n", *args, **kwargs):
super().__init__(*args, **kwargs)
self["status"] = status
self["headers"] = headers
@ -125,15 +124,16 @@ class HttpResponse(AccessDict):
def __str__(self):
buf = StringIO()
print(self.headers)
buf.write(f"HTTP/1.1 {self.status}\r\n")
buf.write(f"HTTP/1.1 {self.status}" + CRLF)
length = len(self["body"].getvalue())
for k, v in self["headers"].items():
buf.write(f"{k}: {v}\r\n")
buf.write(f"Content-Length: {length}\r\n")
buf.write(CRLF + CRLF) # Per RFC 9112
if "Content-Length" not in self["headers"]:
buf.write(f"Content-Length: {length}\r\n")
buf.write(CRLF) # Per RFC 9112
buf.write(self["body"].getvalue() + "\r\n")
return buf.getvalue() + "\r\n"
buf.write(self["body"].getvalue() + CRLF)
return buf.getvalue() + CRLF
RICKROLL_LYRICS = """
@ -170,6 +170,10 @@ Never gonna make you cry, never gonna say goodbye
Never gonna tell a lie and hurt you
"""
head_response = HttpResponse()
head_response.status = 200
head_response.headers["Content-Length"] = 980
head_response.write("")
good_response = HttpResponse()
good_response.status = 200
good_response.headers["Last-Modified"] = "Mon, 27 July 1987 00:00 GMT"
@ -209,7 +213,7 @@ def server_handler():
return
# I made two simple changes to make it use our new http objects
# I made three simple changes to make it use our new http objects
# This will still accept anything as long as the method verb
# is correct. Clients need not conform to RFC 9112 (yet).
# We however, do our best effort to conform to rfc 9112,
@ -219,8 +223,19 @@ def server_handler():
# the client, but strict in what you send back, was first
# was first forumlated by John Postel in that later half
# of the 1970s.
#
# Lastly we add support for the HEAD method, as some http clients
# Will get confused if we don't have it
# RFC 9112 appears to say GET and HEAD are the only methods we
# are ABSOLUTELY REQUIRED to support. So we add it.abs
# At this point it will get a static response as well.
# See Above.
# Doing this, is also motivation for me to write Parts 4, 5, and 6
# Doing this, is also motivation for me to write Parts 1, 2, and 3
# By the way you're in a Star Wars, Sort of thing
# This is Part 5. I thought the 4 would be more entertaining.abs
# Parts 7, 8, and 9 will be made for Capitalism reasons
# AKA Donate on my kofi link at the end.
def client_handler(sock):
@ -234,11 +249,16 @@ def client_handler(sock):
dstring = data.decode("UTF-8")
if dstring.startswith("GET"):
break
elif dstring.startswith("HEAD"):
hr = str(head_response)
sock.send(hr.encode("utf-8"))
sock.close()
return
else:
error = str(error_response)
sock.send(error.encode("utf-8"))
junk_counter += 1
gevent.sleep(0.25)
gevent.sleep(0.25) # this is a somewhat magical value
default = str(good_response)
sock.send(default.encode("utf-8"))
sock.close()