壳把请求翻译成什么
(每道题开头都有同一段:上面那个应用的全部源码——render 把标题和正文塞进页面骨架 PAGE,list_html / form_html 生成列表和表单,STATIC 是静态文件表,handle 是入口。判题机不联网,题里直接调 handle。)
真机上的壳(标准库 http.server):
from http.server import BaseHTTPRequestHandler, HTTPServer
class H(BaseHTTPRequestHandler):
def _serve(self, method):
n = int(self.headers.get("Content-Length") or 0)
body = self.rfile.read(n).decode("utf-8") if n else ""
status, headers, text = handle(method, self.path, body, NOTES)
data = text.encode("utf-8")
self.send_response(status)
for k, v in headers.items():
self.send_header(k, v + ("; charset=utf-8" if k == "Content-Type" and v.startswith("text/") else ""))
self.send_header("Content-Length", str(len(data)))
self.end_headers()
if method != "HEAD":
self.wfile.write(data)
def do_GET(self):
self._serve("GET")
def do_POST(self):
self._serve("POST")
def do_HEAD(self):
self._serve("HEAD")
NOTES = []
HTTPServer(("127.0.0.1", 8000), H).serve_forever()没有网络也能看壳的逻辑:模拟一次「读请求 → 调 handle → 拼响应」,看拼出来的头:
import html
import json
from urllib.parse import urlparse, parse_qs
PAGE = "<!doctype html><html><head><meta charset=\"utf-8\"><title>{title}</title><link rel=\"stylesheet\" href=\"/static/style.css\"></head><body><h1>{title}</h1>{body}</body></html>"
def render(title, body):
return PAGE.replace("{title}", html.escape(title)).replace("{body}", body)
def list_html(notes):
if not notes:
return "<p>还没有留言</p>"
return "<ul>" + "".join("<li>" + html.escape(n) + "</li>" for n in notes) + "</ul>"
def form_html():
return "<form method=\"post\" action=\"/add\"><input name=\"text\"><button>发表</button></form>"
STATIC = {"/static/style.css": ("text/css", "h1 { color: #2563eb; }\n")}
def handle(method, url, body="", notes=None):
notes = [] if notes is None else notes
u = urlparse(url)
path = u.path
if path in STATIC:
if method != "GET":
return 405, {"Content-Type": "text/html"}, "<p>只能 GET</p>"
ctype, content = STATIC[path]
return 200, {"Content-Type": ctype}, content
if path.startswith("/static/"):
return 404, {"Content-Type": "text/html"}, "<p>没有这个文件</p>"
if path == "/":
return 200, {"Content-Type": "text/html"}, render("留言板", list_html(notes) + form_html())
if path == "/about":
return 200, {"Content-Type": "text/html"}, render("关于", "<p>一个用标准库写的留言板。</p>")
if path.startswith("/notes/"):
tail = path[len("/notes/"):]
if not tail.isdigit() or not 1 <= int(tail) <= len(notes):
return 404, {"Content-Type": "text/html"}, render("没有这条", "<p>没有这条留言</p>")
return 200, {"Content-Type": "text/html"}, render("第 " + tail + " 条", "<p>" + html.escape(notes[int(tail) - 1]) + "</p>")
if path == "/api/notes":
return 200, {"Content-Type": "application/json"}, json.dumps({"count": len(notes), "notes": notes}, ensure_ascii=False)
if path == "/add":
if method != "POST":
return 405, {"Content-Type": "text/html"}, "<p>请用表单提交</p>"
text = parse_qs(body).get("text", [""])[0].strip()
if not text:
return 400, {"Content-Type": "text/html"}, "<p>留言不能为空</p>"
notes.append(text)
return 303, {"Content-Type": "text/html", "Location": "/"}, ""
return 404, {"Content-Type": "text/html"}, render("找不到", "<p>没有这个页面</p>")
SAMPLE = ["第一条留言", "今天天气不错", "<b>不许</b>"]
def build_response(method, path, body, notes):
status, headers, text = handle(method, path, body, notes)
data = text.encode("utf-8")
lines = ["HTTP/1.0 " + str(status)]
for k, v in headers.items():
lines.append(k + ": " + v + ("; charset=utf-8" if k == "Content-Type" and v.startswith("text/") else ""))
lines.append("Content-Length: " + str(len(data)))
return lines
print(("|".join(build_response("GET", "/api/notes", "", list(SAMPLE))) + "//" + build_response("GET", "/", "", list(SAMPLE))[1]).replace(" ", "_"))
全部评论