Skip to content

Commit 47aeae0

Browse files
committed
Python HTTP Server: Apply TR Feedback (materials)
1 parent 9327852 commit 47aeae0

File tree

9 files changed

+32
-255
lines changed

9 files changed

+32
-255
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python3
2+
3+
print(
4+
"""\
5+
Content-Type: text/html
6+
7+
<!DOCTYPE html>
8+
<html lang="en">
9+
<body>
10+
<h1>Hello, World!</h1>
11+
</body>
12+
</html>"""
13+
)

python-http-server/webapp/static/app.js

Lines changed: 0 additions & 7 deletions
This file was deleted.
-8.25 KB
Binary file not shown.

python-http-server/webapp/static/style.css

Lines changed: 0 additions & 76 deletions
This file was deleted.

python-http-server/webapp/templates/home_view.html

Lines changed: 0 additions & 20 deletions
This file was deleted.

python-http-server/webapp/templates/login_form.html

Lines changed: 0 additions & 20 deletions
This file was deleted.

python-http-server/webapp/users.json

Lines changed: 0 additions & 4 deletions
This file was deleted.
Lines changed: 19 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import json
2-
import mimetypes
3-
import webbrowser
42
from functools import cached_property
53
from http.cookies import SimpleCookie
64
from http.server import BaseHTTPRequestHandler, HTTPServer
7-
from pathlib import Path
85
from urllib.parse import parse_qsl, urlparse
96

107

@@ -14,12 +11,12 @@ def url(self):
1411
return urlparse(self.path)
1512

1613
@cached_property
17-
def query_data(self):
14+
def query_data(self) -> dict[str, str]:
1815
return dict(parse_qsl(self.url.query))
1916

2017
@cached_property
21-
def post_data(self):
22-
content_length = int(self.headers["Content-Length"])
18+
def post_data(self) -> bytes:
19+
content_length = int(self.headers.get("Content-Length", 0))
2320
return self.rfile.read(content_length)
2421

2522
@cached_property
@@ -31,134 +28,28 @@ def cookies(self):
3128
return SimpleCookie(self.headers.get("Cookie"))
3229

3330
def do_GET(self):
34-
self.send_error(404)
35-
36-
def do_POST(self):
37-
self.send_error(404)
38-
39-
40-
class StaticView(WebRequestHandler):
41-
STATIC_DIR = Path("static")
42-
STATIC_PATH = "/static"
43-
44-
def do_GET(self):
45-
if self.url.path.startswith(self.STATIC_PATH):
46-
self.send_file(self.STATIC_DIR / Path(self.url.path).name)
47-
else:
48-
super().do_GET()
49-
50-
def send_file(self, path, encoding="utf-8"):
51-
if path.exists() and path.is_file():
52-
mime_type, _ = mimetypes.guess_type(path)
53-
if mime_type.startswith("text/"):
54-
content_type = f"{mime_type}; charset={encoding}"
55-
else:
56-
content_type = mime_type
57-
self.send_response(200)
58-
self.send_header("Content-Type", content_type)
59-
self.end_headers()
60-
self.wfile.write(path.read_bytes())
61-
else:
62-
self.send_error(404)
63-
64-
65-
class DynamicView(WebRequestHandler):
66-
TEMPLATES_DIR = Path("templates")
67-
68-
def send_redirect(self, location, extra_headers=None):
69-
self.send_response(302)
70-
self.send_header("Location", location)
71-
if extra_headers:
72-
for name, value in extra_headers.items():
73-
self.send_header(name, value)
74-
self.end_headers()
75-
76-
def render_template(self, name, encoding="utf-8", **context):
7731
self.send_response(200)
78-
self.send_header("Content-Type", f"text/html; charset={encoding}")
79-
self.send_header("Cache-Control", "no-store, must-revalidate")
80-
self.send_header("Pragma", "no-cache")
81-
self.send_header("Expires", "0")
32+
self.send_header("Content-Type", "application/json")
8233
self.end_headers()
83-
template = (self.TEMPLATES_DIR / name).read_text(encoding)
84-
self.wfile.write(template.format(**context).encode(encoding))
85-
86-
87-
class MyAppView(StaticView, DynamicView):
88-
SESSION_COOKIE = "session"
89-
USERS_DATABASE = Path("users.json")
90-
91-
def do_GET(self):
92-
match self.url.path:
93-
case "/":
94-
self.home_view()
95-
case "/login":
96-
self.login_view()
97-
case "/logout":
98-
self.logout_view()
99-
case _:
100-
super().do_GET()
34+
self.wfile.write(self.get_response().encode("utf-8"))
10135

10236
def do_POST(self):
103-
if self.url.path == "/login":
104-
self.handle_login_form()
105-
else:
106-
super().do_POST()
107-
108-
@property
109-
def users(self):
110-
with self.USERS_DATABASE.open(encoding="utf-8") as file:
111-
return json.load(file)
112-
113-
@property
114-
def logged_in(self):
115-
return self.SESSION_COOKIE in self.cookies
116-
117-
@property
118-
def logged_user(self):
119-
return self.cookies[self.SESSION_COOKIE].value
120-
121-
def home_view(self):
122-
if self.logged_in:
123-
self.render_template("home_view.html", username=self.logged_user)
124-
else:
125-
self.send_redirect("/login")
126-
127-
def login_view(self):
128-
if self.logged_in:
129-
self.send_redirect("/")
130-
else:
131-
hidden = "" if "failed" in self.query_data else "hidden"
132-
self.render_template("login_form.html", hidden=hidden)
133-
134-
def logout_view(self):
135-
self.send_redirect(
136-
"/login",
137-
extra_headers={"Set-Cookie": f"{self.SESSION_COOKIE}=; Max-Age=0"},
37+
self.do_GET()
38+
39+
def get_response(self):
40+
return json.dumps(
41+
{
42+
"path": self.url.path,
43+
"query_data": self.query_data,
44+
"post_data": self.post_data.decode("utf-8"),
45+
"form_data": self.form_data,
46+
"cookies": {
47+
name: cookie.value for name, cookie in self.cookies.items()
48+
},
49+
}
13850
)
13951

140-
def handle_login_form(self):
141-
try:
142-
username = self.form_data["username"]
143-
password = self.form_data["password"]
144-
except KeyError:
145-
self.send_error(400)
146-
else:
147-
if self.authenticate(username, password):
148-
self.send_redirect(
149-
"/",
150-
extra_headers={
151-
"Set-Cookie": f"{self.SESSION_COOKIE}={username}"
152-
},
153-
)
154-
else:
155-
self.send_redirect("/login?failed=true")
156-
157-
def authenticate(self, username, password):
158-
return password == self.users.get(username)
159-
16052

16153
if __name__ == "__main__":
162-
server = HTTPServer(("0.0.0.0", 8000), MyAppView)
163-
webbrowser.open("http://{0}:{1}".format(*server.server_address))
54+
server = HTTPServer(("0.0.0.0", 8000), WebRequestHandler)
16455
server.serve_forever()

0 commit comments

Comments
 (0)