Skip to content

Commit 4e6f640

Browse files
committed
fix: small issues with chunks and comments
1 parent 46133d1 commit 4e6f640

File tree

11 files changed

+71
-101
lines changed

11 files changed

+71
-101
lines changed

src/Std/Internal/Async/Basic.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ protected def pure (x : α) : ETask ε α :=
137137
Task.pure <| .ok x
138138

139139
/--
140-
Returns `true` if a `ETask` has finished it's execution
140+
Returns `true` if a `ETask` has finished its execution
141141
-/
142142
@[inline]
143143
protected def isFinished (x : ETask ε α) : BaseIO Bool := do

src/Std/Internal/Http.lean

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@ A "low-level" HTTP 1.1 Server and Client implementation for Lean. It is designed
2020
# Overview
2121
2222
This module of the standard library defines many concepts related to the HTTP protocol
23-
and its semantics in a SANS-IO format. The main function of this library is `Std.Http.Server.serve`,
23+
and its semantics in a sans I/O format.
24+
25+
**sans I/O** means that the core logic of the library doesn’t perform any actual input/output itself,
26+
it just defines how data *should* be processed. This separation allows the protocol implementation
27+
to remain pure and testable, while different transports (like TCP sockets or mocks) can handle
28+
the actual reading and writing of bytes.
29+
30+
The main function of this library is `Std.Http.Server.serve`,
2431
located in the module `Std.Internal.Http.Server`. It starts a simple HTTP/1.1 server that
2532
handles all requests and sends them to a simple handler function. It uses the default `Std.Internal.Async`
2633
library, but it can be customized to use whatever IO library you want, as the protocol implementation
@@ -29,14 +36,6 @@ is pure.
2936
If you want to customize how your server handles sockets, you can use `Std.Http.Server.serveConnection`,
3037
which is a simple function to bind a handler to a `Transport`.
3138
32-
# Low-Level Protocol Implementation
33-
34-
This library provides a low-level foundation that allows you to implement your own IO layer on top
35-
of it. The core protocol parsing and generation logic is available in `Std.Internal.Http.Protocol`,
36-
which provides pure functions for HTTP message parsing and serialization. This design allows you to
37-
integrate the HTTP protocol handling with any IO system or networking library of your choice, while
38-
reusing the robust protocol implementation.
39-
4039
# Minimal Example
4140
4241
```lean
@@ -63,26 +62,19 @@ def main := mainAsync.block
6362
6463
## Transport
6564
66-
`Std.Http.Server.Transport` is a type class used for describing a way of communication between a `Connection` and outside
67-
of the `Connection`. It can be a `Mock.Client` that sends and receives byte arrays so it can be used for
68-
deterministic testing a HTTP connection or a `TCP.Socket.Client` that is how usually it communicated with
69-
the internet.
65+
`Std.Http.Server.Transport` is a type class that defines how communication occurs between a `Connection`
66+
and the outside world. It can be implemented by, for example, a `Mock.Client`, which sends and receives
67+
byte arrays for deterministic HTTP connection testing, or by a `TCP.Socket.Client`, which is the
68+
standard way to communicate over the internet in HTTP/1.1.
7069
7170
## Connection
7271
73-
`Std.Http.Server.Connection` is a structure that stores both a `Transport` and a `Machine`
74-
the machine right now is only a `Protocol.H1.Machine` that implements a State Machine for parsing request and responses for HTTP/1.1
72+
`Std.Http.Server.Connection` is a structure that holds both a `Transport` and a `Machine`. Currently,
73+
the machine is a `Protocol.H1.Machine`, which implements the state machine responsible for parsing HTTP/1.1
74+
requests and responses.
7575
7676
If you want to customize how your server handles sockets, you can use `Std.Http.Server.serveConnection`,
77-
which is a simple function to bind a handler to a `ClientConnection`.
78-
79-
# Low-Level Protocol Implementation
80-
81-
This library provides a low-level foundation that allows you to implement your own IO layer on top
82-
of it. The core protocol parsing and generation logic is available in `Std.Internal.Http.Protocol`,
83-
which provides pure functions for HTTP message parsing and serialization. This design allows you to
84-
integrate the HTTP protocol handling with any IO system or networking library of your choice, while
85-
reusing the robust protocol implementation.
77+
a simple function that binds a handler to a `ClientConnection`.
8678
8779
# Minimal Example
8880
@@ -100,10 +92,9 @@ def handler (req : Request Body) : Async (Response Body) := do
10092
return Response.ok ("hi, " ++ data)
10193
10294
def mainAsync : Async Unit := do
103-
Server.serve (.v4 (.mk (.ofParts 0 0 0 0) 8080)) handler
95+
let server ← Server.serve (.v4 (.mk (.ofParts 0 0 0 0) 8080)) handler
96+
server.waitShutdown
10497
10598
def main := mainAsync.block
10699
```
107100
-/
108-
109-
namespace Std.Http

src/Std/Internal/Http/Client/Config.lean

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,13 @@ namespace Config
8686
/--
8787
Convert this client config into an HTTP/1.1 protocol configuration.
8888
-/
89-
def toH1Config (config : Config) : Protocol.H1.Config :=
90-
{ maxMessages := config.maxRequestsPerConnection
91-
maxHeaders := config.maxResponseHeaders
92-
maxHeaderSize := config.maxHeaderValueSize
93-
enableKeepAlive := config.enableKeepAlive
94-
highMark := config.writeBufferHighWatermark
95-
identityHeader := config.userAgent
96-
}
89+
def toH1Config (config : Config) : Protocol.H1.Config where
90+
maxMessages := config.maxRequestsPerConnection
91+
maxHeaders := config.maxResponseHeaders
92+
maxHeaderSize := config.maxHeaderValueSize
93+
enableKeepAlive := config.enableKeepAlive
94+
highMark := config.writeBufferHighWatermark
95+
identityHeader := config.userAgent
9796

9897
end Config
9998
end Std.Http.Client

src/Std/Internal/Http/Client/Connection.lean

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ namespace PersistentConnection
103103
/--
104104
Send a request through the persistent connection.
105105
-/
106-
def send [Transport α] (pc : PersistentConnection α) (request : Request Body) : Async (Response Body) := do
106+
def send (pc : PersistentConnection α) (request : Request Body) : Async (Response Body) := do
107107
let responsePromise ← IO.Promise.new
108108
let cancellationToken ← Std.CancellationToken.new
109109

@@ -278,22 +278,15 @@ Create a persistent connection that can handle multiple sequential requests.
278278
# Example
279279
280280
```lean
281-
-- Connect to a server
282-
let socket ← Socket.mk
283-
socket.connect serverAddr
281+
let client ← TCP.Socket.Client.mk
282+
client.connect addr
284283
285-
-- Create persistent connection
286-
let pc ← createPersistentConnection socket config
284+
let persistent ← Client.createPersistentConnection client
287285
288-
-- Spawn the connection handler
289-
async (pc.connection.handlePersistent pc.config pc.requestChannel)
286+
let res1 ← persistent.send req1
287+
let res2 ← persistent.send req2
290288
291-
-- Send multiple requests
292-
pc.send request1 (fun response => IO.println s!"Response 1: {response.head.status}")
293-
pc.send request2 (fun response => IO.println s!"Response 2: {response.head.status}")
294-
295-
-- Close when done
296-
pc.close
289+
persistent.close
297290
```
298291
-/
299292
def createPersistentConnection [Transport t] (client : t) (config : Client.Config := {}) : Async (PersistentConnection t) := do

src/Std/Internal/Http/Data/Body.lean

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ namespace Std.Http
2121

2222
open Std Internal IO Async
2323

24-
set_option linter.all true
25-
2624
/--
2725
Type that represents the body of a request or response with streams of bytearrays or bytearrays of fixed
2826
size.

src/Std/Internal/Http/Data/Body/ByteStream.lean

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -164,38 +164,29 @@ partial def write (stream : ByteStream) (data : ByteArray) (extensions : Array (
164164

165165
let availableSpace := state.maxBufferSize - state.bufferedSize
166166

167-
if availableSpace = 0 then
167+
if availableSpace < data.size then
168168
let promise ← IO.Promise.new
169169
modify fun s => { s with backpressureWaiting := s.backpressureWaiting.enqueue promise }
170170
return Sum.inl (promise, data, extensions)
171171
else
172-
let toWrite := min data.size availableSpace
173-
let dataToAdd := data.extract 0 toWrite
174-
let remaining := if toWrite < data.size then data.extract toWrite data.size else ByteArray.empty
175-
176-
let chunk : Chunk := {
177-
data := dataToAdd,
178-
extensions := extensions
179-
}
172+
let chunk : Chunk := { data := data, extensions := extensions }
180173

181174
state := { state with
182175
pendingChunks := state.pendingChunks.push chunk,
183176
bufferedSize := state.bufferedSize + chunk.size
184177
}
185178

186-
-- Notify waiting consumers if we've reached highMark
187179
if state.bufferedSize >= state.highMark then
188180
if let some (consumer, rest) := state.waiting.dequeue? then
189181
discard <| consumer.resolve true
190182
state := { state with waiting := rest }
191183

192184
set state
193-
return Sum.inr (true, remaining, extensions)
185+
return Sum.inr (true, ByteArray.empty, extensions)
194186

195187
match result with
196188
| .inr (success, remaining, exts) =>
197-
if remaining.isEmpty ∨ ¬ success
198-
then
189+
if remaining.isEmpty ∨ ¬ success then
199190
return success
200191
else
201192
write stream remaining exts
@@ -346,24 +337,23 @@ Iterate over the body content in chunks, processing each ByteArray chunk with th
346337
-/
347338
@[inline]
348339
protected partial def forIn
349-
{β : Type} (stream : ByteStream) (acc : β)
350-
(step : Chunk → β → Async (ForInStep β)) :
351-
Async β := do
352-
let rec @[specialize] loop (stream : ByteStream) (acc : β) : Async β := do
340+
{β : Type} (stream : ByteStream) (acc : β)
341+
(step : Chunk → β → Async (ForInStep β)) : Async β := do
353342

354-
if let some data ← stream.recv then
355-
let mut acc := acc
343+
let rec @[specialize] loop (stream : ByteStream) (acc : β) : Async β := do
344+
if let some data ← stream.recv then
345+
let mut acc := acc
356346

357-
for data in data do
358-
match ← step data acc with
359-
| .done res => return res
360-
| .yield res => acc := res
347+
for data in data do
348+
match ← step data acc with
349+
| .done res => return res
350+
| .yield res => acc := res
361351

362-
loop stream acc
363-
else
364-
return acc
352+
loop stream acc
353+
else
354+
return acc
365355

366-
loop stream acc
356+
loop stream acc
367357

368358
instance : ForIn Async ByteStream Chunk where
369359
forIn := Std.Http.Body.ByteStream.forIn

src/Std/Internal/Http/Data/Body/Length.lean

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ namespace Std.Http.Body
2121

2222
set_option linter.all true
2323

24-
set_option linter.all true
25-
2624
/--
2725
Size of the body of a response or request.
2826
-/

src/Std/Internal/Http/Data/Chunk.lean

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ Represents a chunk of data with optional extensions (key-value pairs).
3030
structure Chunk where
3131

3232
/--
33-
?
33+
The byte data contained in this chunk.
3434
-/
3535
data : ByteArray
3636

3737
/--
38-
?
38+
Optional metadata associated with this chunk as key-value pairs. Keys are strings, values are
39+
optional strings.
3940
-/
4041
extensions : Array (String × Option String) := #[]
4142
deriving Inhabited
@@ -59,14 +60,14 @@ Returns the total size of the chunk including data and formatted extensions. Ext
5960
as: ;name=value;name=value Plus 2 bytes for \r\n at the end.
6061
-/
6162
def size (chunk : Chunk) : Nat :=
62-
let extensionsSize := chunk.extensions.foldl (fun acc (name, value) => acc + name.length + (value.get!).length + 2) 0
63+
let extensionsSize := chunk.extensions.foldl (fun acc (name, value) => acc + name.length + (value.map (fun v => v.length + 1) |>.getD 0) + 1) 0
6364
chunk.data.size + extensionsSize + (if extensionsSize > 0 then 2 else 0)
6465

6566
instance : Encode .v11 Chunk where
6667
encode buffer chunk :=
67-
let chunkLen := chunk.data.size
68-
let exts := chunk.extensions.foldl (fun acc (name, value) => acc ++ ";" ++ name ++ (value.map (fun x => "=" ++ x) |>.getD "")) ""
69-
let size := Nat.toDigits 16 chunkLen |>.toArray |>.map Char.toUInt8 |> ByteArray.mk
70-
buffer.append #[size, exts.toUTF8, "\r\n".toUTF8, chunk.data, "\r\n".toUTF8]
68+
let chunkLen := chunk.data.size
69+
let exts := chunk.extensions.foldl (fun acc (name, value) => acc ++ ";" ++ name ++ (value.map (fun x => "=" ++ x) |>.getD "")) ""
70+
let size := Nat.toDigits 16 chunkLen |>.toArray |>.map Char.toUInt8 |> ByteArray.mk
71+
buffer.append #[size, exts.toUTF8, "\r\n".toUTF8, chunk.data, "\r\n".toUTF8]
7172

7273
end Chunk

src/Std/Internal/Http/Data/Headers.lean

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,13 @@ Returns `true` if they match.
106106
def is (s : HeaderValue) (h : String) : Bool :=
107107
s.value.toLower == h.toLower
108108

109+
109110
/--
110111
Concatenates two `HeaderValue` instances, preserving the validity guarantee.
111112
-/
112-
def append (l : HeaderValue) (r : HeaderValue) : HeaderValue := by
113-
refine ⟨l.value ++ r.value, ?_⟩
113+
def append (l : HeaderValue) (r : HeaderValue) : HeaderValue :=
114+
⟨l.value ++ r.value, ?_⟩
115+
where finally
114116
unfold isValidHeaderValue
115117
rw [String.data_append]
116118
rw [List.all_append]

src/Std/Internal/Http/Server.lean

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,8 @@ private def frameCancellation (s : Server) (action : Async α) : Async α := do
109109
return result
110110

111111
/--
112-
Start a new HTTP/1.1 server on the given socket address. This function uses the `Async` for handling
113-
tasks and TCP connections, it also returns a `Server` structure that can be used to the cancellation
114-
of the server.
112+
Start a new HTTP/1.1 server on the given socket address. This function uses `Async` to handle tasks
113+
and TCP connections, and returns a `Server` structure that can be used to cancel the server.
115114
-/
116115
def serve
117116
(addr : Net.SocketAddress)

0 commit comments

Comments
 (0)