lean4-htt/tests/elab/async_http_request_line.lean
Sofia Rodrigues a0b2e1f302
feat: introduce HTTP/1.1 server (#12151)
This PR introduces the Server module, an Async HTTP/1.1 server.

This contains the same code as #10478, divided into separate pieces to
facilitate easier review.

The pieces of this feature are:
- Core data structures: #12126
- Headers: #12127
- URI:  #12128
- Body: #12144
- H1: #12146
- Server: #12151
- Client:

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2026-04-20 16:25:45 +00:00

166 lines
6 KiB
Text

import Std.Internal.Http.Test.Helpers
open Std.Internal.IO Async
open Std Http Internal Test
-- Shared fixtures
private def ok200 : String :=
"HTTP/1.1 200 OK\x0d\nContent-Type: text/plain; charset=utf-8\x0d\nServer: LeanHTTP/1.1\x0d\nConnection: close\x0d\nContent-Length: 2\x0d\n\x0d\nok"
-- RFC 9112 §3: Request Line
#eval runGroup "RFC 9112 §3: request-line parse failures" do
check "missing version → 400"
(raw := "GET /\x0d\nHost: example.com\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "missing URI (double space) → 400"
(raw := "GET HTTP/1.1\x0d\nHost: example.com\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "extra spaces in request-line → 400"
(raw := "GET / HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "whitespace-only request-line → 400"
(raw := " \x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "no spaces in request-line → 400"
(raw := "GETHTTP/1.1\x0d\nHost: example.com\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "garbage after request-line version → 400"
(raw := "GET / HTTP/1.1 xxxxxx\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
-- Empty connection: no bytes → silent close, no response
checkClose "empty connection → silent close"
(raw := "")
(handler := okHandler)
(expect := fun r => assertExact r "")
#eval runGroup "RFC 9112 §2.2: leading CRLF before request-line" do
check "single leading CRLF accepted"
(raw := "\x0d\nGET / HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r ok200)
-- RFC 9112 §9: HTTP version
#eval runGroup "RFC 9112 §9: HTTP version" do
check "HTTP/2.0 → 505"
(raw := "GET / HTTP/2.0\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r505)
-- RFC 9110 §9: Methods
#eval runGroup "RFC 9110 §9: method validation" do
check "unknown method FOOBAR → 400"
(raw := "FOOBAR / HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "lowercase method → 400"
(raw := "get / HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "non-ASCII method → 400"
(raw := "GÉT / HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "very long unrecognized method → 400"
(raw :=
let m := String.ofList (List.replicate 20 'G')
s!"{m} / HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "token method with hyphen (X-CUSTOM) → 400"
(raw := "X-CUSTOM / HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
-- RFC 9112 §3.2: Request target forms
#eval runGroup "RFC 9112 §3.2: request target forms" do
check "GET authority-form → 400"
(raw := "GET example.com:443 HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "CONNECT authority-form accepted"
(raw := "CONNECT example.com:443 HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r ok200)
check "CONNECT authority-form port mismatch → 400"
(raw := "CONNECT example.com:444 HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "GET asterisk-form → 400"
(raw := "GET * HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "OPTIONS * accepted"
(raw := "OPTIONS * HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r ok200)
check "absolute-form URI accepted"
(raw := "GET http://example.com/path HTTP/1.1\x0d\nHost: example.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r ok200)
-- RFC 9112 §3.3: Early invalid bytes
#eval runGroup "RFC 9112 §3: early invalid bytes" do
checkClose "NUL byte → 400"
(raw := "\x00")
(handler := okHandler)
(expect := fun r => assertExact r r400)
checkClose "SP byte → 400"
(raw := "\x20")
(handler := okHandler)
(expect := fun r => assertExact r r400)
checkClose "TLS client-hello prefix → 400"
(raw := "\x16\x03\x01\x00\xa5")
(handler := okHandler)
(expect := fun r => assertExact r r400)
-- RFC 7230 §5.4: Host header
#eval runGroup "RFC 7230 §5.4: Host header" do
check "missing Host header → 400"
(raw := "GET / HTTP/1.1\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "empty Host allowed for origin-form"
(raw := "GET / HTTP/1.1\x0d\nHost: \x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r ok200)
check "multiple Host headers → 400"
(raw := "GET / HTTP/1.1\x0d\nHost: example.com\x0d\nHost: other.com\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r r400)
check "absolute-form: URI authority takes precedence over Host"
(raw := "GET http://good.example/path HTTP/1.1\x0d\nHost: good.example\x0d\nConnection: close\x0d\n\x0d\n")
(handler := okHandler)
(expect := fun r => assertExact r ok200)