Skip to content

Commit b6fe496

Browse files
committed
fix: trailer
1 parent fdc4d91 commit b6fe496

File tree

1 file changed

+68
-76
lines changed

1 file changed

+68
-76
lines changed

tests/lean/run/async_http_raw.lean

Lines changed: 68 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,17 @@ structure Result where
99
data : ByteArray
1010

1111
/-- Convert an HTTP request to a byte array, optionally using chunked encoding. -/
12-
def toByteArray (req : Request (Array Chunk)) (chunked := false) : IO ByteArray := Async.block do
12+
def requestToByteArray (req : Request (Array Chunk)) (chunked := false) : IO ByteArray := Async.block do
1313
let mut data := String.toUTF8 <| toString req.head
1414
let toByteArray (part : Chunk) := Internal.Encode.encode .v11 .empty part |>.toByteArray
1515
for part in req.body do data := data ++ (if chunked then toByteArray part else part.data)
1616
if chunked then data := data ++ toByteArray (Chunk.mk .empty .empty)
1717
return data
1818

19-
/-- Send multiple requests through a mock connection and return the response data. -/
20-
def sendRequests (pair : Mock.Client × Mock.Server) (reqs : Array (Request (Array Chunk)))
21-
(onRequest : Request Body → Async (Response Body))
22-
(chunked : Bool := false) : IO ByteArray := Async.block do
19+
/-- Send raw byte stream through a mock connection and return the response data. -/
20+
def sendRawBytes (pair : Mock.Client × Mock.Server) (data : ByteArray)
21+
(onRequest : Request Body → Async (Response Body)) : IO ByteArray := Async.block do
2322
let (client, server) := pair
24-
let mut data := .empty
25-
for req in reqs do data := data ++ (← toByteArray req chunked)
2623

2724
client.send data
2825
Std.Http.Server.serveConnection server onRequest (config := { lingeringTimeout := 3000 })
@@ -45,24 +42,29 @@ def testSizeLimit (pair : Mock.Client × Mock.Server) : IO Unit := do
4542
|>.status .ok
4643
|>.body "hello robert"
4744

48-
let response ← sendRequests pair #[
45+
-- Manually construct the byte stream
46+
let mut data := .empty
47+
data := data ++ (← requestToByteArray (
4948
Request.new
5049
|>.uri! "/ata/po"
5150
|>.header! "Content-Length" "4"
5251
|>.header! "Host" "."
53-
|>.body #[.mk "test".toUTF8 #[]],
52+
|>.body #[.mk "test".toUTF8 #[]]))
53+
data := data ++ (← requestToByteArray (
5454
Request.new
5555
|>.uri! "/ata/po"
5656
|>.header! "Content-Length" "13"
5757
|>.header! "Connection" "close"
5858
|>.header! "Host" "."
59-
|>.body #[.mk "testtesttests".toUTF8 #[]],
59+
|>.body #[.mk "testtesttests".toUTF8 #[]]))
60+
data := data ++ (← requestToByteArray (
6061
Request.new
6162
|>.uri! "/ata/po"
6263
|>.header! "Content-Length" "4"
6364
|>.header! "Host" "."
64-
|>.body #[.mk "test".toUTF8 #[]],
65-
] handler
65+
|>.body #[.mk "test".toUTF8 #[]]))
66+
67+
let response ← sendRawBytes pair data handler
6668

6769
let responseData := String.fromUTF8! response
6870
IO.println <| String.quote responseData
@@ -83,16 +85,17 @@ def testBasicRequest : IO Unit := do
8385
|>.header! "Connection" "close"
8486
|>.body "Hello World"
8587

86-
let responsesendRequests pair #[
88+
let datarequestToByteArray (
8789
Request.new
8890
|>.uri! "/hello"
8991
|>.header! "Host" "localhost"
9092
|>.header! "User-Agent" "TestClient/1.0"
9193
|>.header! "Connection" "close"
9294
|>.header! "Content-Length" "0"
9395
|>.method .get
94-
|>.body #[.mk "".toUTF8 #[]]
95-
] handler
96+
|>.body #[.mk "".toUTF8 #[]])
97+
98+
let response ← sendRawBytes pair data handler
9699

97100
let responseData := String.fromUTF8! response
98101
IO.println s!"{responseData.quote}"
@@ -117,16 +120,17 @@ def testPostRequest : IO Unit := do
117120
|>.header! "Connection" "close"
118121
|>.body s!"Received: {body}"
119122

120-
let responsesendRequests pair #[
123+
let datarequestToByteArray (
121124
Request.new
122125
|>.uri! "/api/data"
123126
|>.method .post
124127
|>.header! "Host" "localhost"
125128
|>.header! "Content-Type" "application/json"
126129
|>.header! "Content-Length" "25"
127130
|>.header! "Connection" "close"
128-
|>.body #[.mk "{\"name\": \"test\", \"id\": 1}".toUTF8 #[]]
129-
] handler
131+
|>.body #[.mk "{\"name\": \"test\", \"id\": 1}".toUTF8 #[]])
132+
133+
let response ← sendRawBytes pair data handler
130134

131135
let responseData := String.fromUTF8! response
132136
IO.println s!"{responseData.quote}"
@@ -146,16 +150,17 @@ def test100Continue : IO Unit := do
146150
|>.status .ok
147151
|>.body "Request processed"
148152

149-
let responsesendRequests pair #[
153+
let datarequestToByteArray (
150154
Request.new
151155
|>.uri! "/"
152156
|>.method .get
153157
|>.header! "Host" "example.com"
154158
|>.header! "Content-Length" "1"
155159
|>.header! "Expect" "100-continue"
156160
|>.header! "Connection" "close"
157-
|>.body #[.mk "a".toUTF8 #[]]
158-
] handler
161+
|>.body #[.mk "a".toUTF8 #[]])
162+
163+
let response ← sendRawBytes pair data handler
159164

160165
let responseData := String.fromUTF8! response
161166
IO.println s!"{responseData.quote}"
@@ -189,16 +194,17 @@ def testMaxRequestSize : IO Unit := do
189194
|> ByteArray.mk
190195
|> String.fromUTF8!
191196

192-
let responsesendRequests pair #[
197+
let datarequestToByteArray (
193198
Request.new
194199
|>.uri! "/upload"
195200
|>.method .post
196201
|>.header! "Host" "localhost"
197202
|>.header! "Content-Type" "text/plain"
198203
|>.header! "Content-Length" s!"{largeData.length}"
199204
|>.header! "Connection" "close"
200-
|>.body #[.mk largeData.toUTF8 #[]]
201-
] handler
205+
|>.body #[.mk largeData.toUTF8 #[]])
206+
207+
let response ← sendRawBytes pair data handler
202208

203209
let responseData := String.fromUTF8! response
204210
IO.println s!"{responseData.quote}"
@@ -209,60 +215,37 @@ info: "HTTP/1.1 413 Request Entity Too Large\x0d\nContent-Length: 48\x0d\nConnec
209215
#guard_msgs in
210216
#eval show IO _ from do testMaxRequestSize
211217

212-
def testCut : IO Unit := do
218+
def testChunkedWithTrailer : IO Unit := do
213219
let pair ← Mock.new
214220

215221
let handler := fun (req : Request Body) => do
216-
if req.head.headers.hasEntry (.new "Accept") "application/json" then
217-
return Response.new
218-
|>.status .accepted
219-
|>.header! "Content-Type" "application/json"
220-
|>.body "{\"message\": \"JSON response\", \"status\": \"accepted\"}"
221-
else if req.head.headers.hasEntry (.new "Accept") "text/xml" then
222-
return Response.new
223-
|>.status .ok
224-
|>.header! "Content-Type" "application/xml"
225-
|>.body "<?xml version=\"1.0\"?><response><message>XML response</message></response>"
226-
else
227-
return Response.new
228-
|>.status .ok
229-
|>.header! "Content-Type" "text/plain"
230-
|>.body "Plain text response"
222+
let mut body := ""
223+
for chunk in req.body do
224+
body := body ++ String.fromUTF8! chunk.data
231225

232-
let response ← sendRequests pair #[
233-
Request.new
234-
|>.uri! "/api/content"
235-
|>.method .post
236-
|>.header! "Host" "localhost"
237-
|>.header! "Accept" "application/json"
238-
|>.header! "Content-Type" "application/json"
239-
|>.header! "Content-Length" "18"
240-
|>.body #[.mk "{\"request\": \"data\"}".toUTF8 #[]],
241-
Request.new
242-
|>.uri! "/api/content"
243-
|>.method .get
244-
|>.header! "Host" "localhost"
245-
|>.header! "Accept" "text/xml"
246-
|>.header! "Content-Length" "1"
247-
|>.body #[.mk "a".toUTF8 #[]]
248-
] handler
226+
let checksum := req.head.headers.getLast? (.new "X-Checksum") |>.map (·.value) |>.getD ""
227+
228+
return Response.new
229+
|>.status .ok
230+
|>.header! "Content-Type" "text/plain"
231+
|>.header! "Connection" "close"
232+
|>.body s!"hello: {body.quote}."
233+
234+
-- Construct raw ByteArray for chunked request with trailer
235+
-- This cannot be done with Request builder since it doesn't support trailers
236+
let rawRequest := "POST / HTTP/1.1\r\nHost: localhost\r\nUser-Agent: TestClient/1.0\r\nTransfer-Encoding: chunked\r\nTrailer: X-Checksum\r\nAccept: */*\r\n\r\n5\r\nHello\r\n6\r\n World\r\n0\r\nX-Checksum: abcd\r\n\r\n"
237+
let data : ByteArray := rawRequest.toUTF8
238+
239+
let response ← sendRawBytes pair data handler
249240

250241
let responseData := String.fromUTF8! response
251242
IO.println s!"{responseData.quote}"
252243

253-
/-
254244
/--
255-
info: "HTTP/1.1 202 Accepted\x0d\nContent-Length: 50\x0d\nServer: LeanHTTP/1.1\x0d\nContent-Type: application/json\x0d\n\x0d\n{\"message\": \"JSON response\", \"status\": \"accepted\"}HTTP/1.1 400 Bad Request\x0d\nConnection: close\x0d\nServer: LeanHTTP/1.1\x0d\n\x0d\n"
245+
info: "HTTP/1.1 200 OK\x0d\nContent-Length: 21\x0d\nConnection: close\x0d\nServer: LeanHTTP/1.1\x0d\nContent-Type: text/plain\x0d\n\x0d\nhello: \"Hello World\"."
256246
-/
257247
#guard_msgs in
258-
#eval show IO _ from do testCut
259-
260-
/--
261-
info: "HTTP/1.1 200 OK\x0d\nContent-Length: 35\x0d\nConnection: close\x0d\nServer: LeanHTTP/1.1\x0d\nContent-Type: application/json\x0d\n\x0d\nReceived: {\"name\": \"test\", \"id\": 1}"
262-
-/
263-
#guard_msgs in
264-
#eval show IO _ from do testPostRequest
265-
-/
248+
#eval show IO _ from do testChunkedWithTrailer
266249

267250
def testContentNegotiation : IO Unit := do
268251
let pair ← Mock.new
@@ -284,27 +267,33 @@ def testContentNegotiation : IO Unit := do
284267
|>.header! "Content-Type" "text/plain"
285268
|>.body "Plain text response"
286269

287-
let response ← sendRequests pair #[
270+
let mut data := .empty
271+
272+
data := data ++ (← requestToByteArray (
288273
Request.new
289274
|>.uri! "/api/content"
290275
|>.method .post
291276
|>.header! "Host" "localhost"
292277
|>.header! "Accept" "application/json"
293278
|>.header! "Content-Type" "application/json"
294279
|>.header! "Content-Length" "19"
295-
|>.body #[.mk "{\"request\": \"data\"}".toUTF8 #[]],
280+
|>.body #[.mk "{\"request\": \"data\"}".toUTF8 #[]]))
281+
282+
data := data ++ (← requestToByteArray (
296283
Request.new
297284
|>.uri! "/api/content"
298285
|>.method .get
299286
|>.header! "Host" "localhost"
300287
|>.header! "Accept" "text/xml"
301288
|>.header! "Content-Length" "1"
302-
|>.body #[.mk "a".toUTF8 #[]]
303-
] handler
289+
|>.body #[.mk "a".toUTF8 #[]]))
290+
291+
let response ← sendRawBytes pair data handler
304292

305293
let responseData := String.fromUTF8! response
306294
IO.println s!"{responseData.quote}"
307295

296+
308297
/--
309298
info: "HTTP/1.1 202 Accepted\x0d\nContent-Length: 50\x0d\nServer: LeanHTTP/1.1\x0d\nContent-Type: application/json\x0d\n\x0d\n{\"message\": \"JSON response\", \"status\": \"accepted\"}HTTP/1.1 200 OK\x0d\nContent-Length: 73\x0d\nServer: LeanHTTP/1.1\x0d\nContent-Type: application/xml\x0d\n\x0d\n<?xml version=\"1.0\"?><response><message>XML response</message></response>"
310299
-/
@@ -331,24 +320,27 @@ def testContentNegotiationError : IO Unit := do
331320
|>.header! "Content-Type" "text/plain"
332321
|>.body "Plain text response"
333322

334-
-- Size is 19 of the first so it's wrong.
335-
let response ← sendRequests pair #[
323+
-- Size is 19 of the first so it's wrong (Content-Length says 18).
324+
let mut data := .empty
325+
data := data ++ (← requestToByteArray (
336326
Request.new
337327
|>.uri! "/api/content"
338328
|>.method .post
339329
|>.header! "Host" "localhost"
340330
|>.header! "Accept" "application/json"
341331
|>.header! "Content-Type" "application/json"
342332
|>.header! "Content-Length" "18"
343-
|>.body #[.mk "{\"request\": \"data\"}".toUTF8 #[]],
333+
|>.body #[.mk "{\"request\": \"data\"}".toUTF8 #[]]))
334+
data := data ++ (← requestToByteArray (
344335
Request.new
345336
|>.uri! "/api/content"
346337
|>.method .get
347338
|>.header! "Host" "localhost"
348339
|>.header! "Accept" "text/xml"
349340
|>.header! "Content-Length" "1"
350-
|>.body #[.mk "a".toUTF8 #[]]
351-
] handler
341+
|>.body #[.mk "a".toUTF8 #[]]))
342+
343+
let response ← sendRawBytes pair data handler
352344

353345
let responseData := String.fromUTF8! response
354346
IO.println s!"{responseData.quote}"

0 commit comments

Comments
 (0)