http.server: HTTP/1.1 requests without Host, with two Host fields or with an invalid Host value are accepted

未关闭
#161,675 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
3/5
预计耗时
1-2 天
新手友好度
35/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
停滞
技术栈
python

调研方向

Start with BaseHTTPRequestHandler.parse_request and trace how it handles HTTP/1.1 headers. The report’s three examples define the cases to check: missing Host, duplicate Host fields, and an invalid Host value. Done means each case receives a 400 Bad Request; note that the issue body references linked PR gh-161696.

由索引模型根据 Issue 内容生成。

描述

stdlib type-bug
Bug report

BaseHTTPRequestHandler.parse_request does not look at the Host field. RFC 9112 section 3.2 states: "A server MUST respond with a 400 (Bad Request) status code to any HTTP/1.1 request message that lacks a Host header field and to any request message that contains more than one Host header field line or a Host header field with an invalid field value."

All three kinds of request below get status 200 and reach the handler. The 400 for a missing Host is from RFC 2616 section 14.23; the 400 for duplicate or invalid Host values was added in RFC 7230 section 5.4 (2014).

import http.server, socket, threading

class Handler(http.server.BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"
    def do_GET(self):
        body = ("Host: %r\n" % self.headers.get_all("Host")).encode()
        self.send_response(200)
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)
    def log_message(self, *args): pass

server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), Handler)
threading.Thread(target=server.serve_forever, daemon=True).start()

for request in [b"GET / HTTP/1.1\r\n\r\n",
                b"GET / HTTP/1.1\r\nHost: a.example\r\nHost: b.example\r\n\r\n",
                b"GET / HTTP/1.1\r\nHost: a b\r\n\r\n"]:
    with socket.create_connection(server.server_address, timeout=2) as s:
        s.sendall(request)
        s.shutdown(socket.SHUT_WR)
        lines = s.makefile("rb").read().split(b"\r\n")
    print(request, "->", lines[0], lines[-1])
server.shutdown()

Output:

b'GET / HTTP/1.1\r\n\r\n' -> b'HTTP/1.1 200 OK' b'Host: None\n'
b'GET / HTTP/1.1\r\nHost: a.example\r\nHost: b.example\r\n\r\n' -> b'HTTP/1.1 200 OK' b"Host: ['a.example', 'b.example']\n"
b'GET / HTTP/1.1\r\nHost: a b\r\n\r\n' -> b'HTTP/1.1 200 OK' b"Host: ['a b']\n"

Expected: HTTP/1.1 400 Bad Request for all three requests.

CPython versions tested on:

3.14

Operating systems tested on:

Linux

Linked PRs
  • gh-161696
主要语言
Python
星标
78.2k
派生
36.6k
平均合并
19 小时 57 分钟
30 天内合并 PR
630

环境准备

在 Codespaces 中打开

在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

python/cpython 的其他 Issue

查看 python/cpython 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。