Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
d0f080e
feat: async type classes
algebraic-dev Sep 12, 2025
bbecd8e
feat: remove outparams
algebraic-dev Sep 20, 2025
e2ff13b
fix: remove useless function
algebraic-dev Sep 20, 2025
23b3d02
fix: stream can only return one type
algebraic-dev Sep 20, 2025
989e934
feat: async traits
algebraic-dev Sep 15, 2025
c5db0a2
fix: small fixes
algebraic-dev Sep 15, 2025
9a1b46d
fix: wrong function
algebraic-dev Sep 20, 2025
29c9e80
fix: channel
algebraic-dev Sep 16, 2025
839d5f3
feat: basics stream map
algebraic-dev Sep 16, 2025
6eed20c
fix: stream map
algebraic-dev Sep 16, 2025
1fbf395
fix: channel
algebraic-dev Sep 16, 2025
a89cf32
fix: async stream
algebraic-dev Sep 20, 2025
d799897
fix: streammap
algebraic-dev Sep 20, 2025
27ecf7c
fix: async
algebraic-dev Sep 24, 2025
feee003
fix: small comments
algebraic-dev Sep 24, 2025
b07f302
fix: async
algebraic-dev Sep 24, 2025
82130c6
fix: simplify
algebraic-dev Sep 22, 2025
d36a197
feat: cancellation
algebraic-dev Sep 22, 2025
e293d1f
fix: streammap
algebraic-dev Sep 25, 2025
49da511
fix: async
algebraic-dev Sep 24, 2025
3bca40c
feat: http
algebraic-dev Sep 20, 2025
3b9d963
fix: copyright notice
algebraic-dev Sep 20, 2025
ac77ef4
fix: coe option
algebraic-dev Sep 20, 2025
d70194d
fix: imports
algebraic-dev Sep 20, 2025
8a5af9a
fix: remove orphaned module
algebraic-dev Sep 20, 2025
b812d5a
fix: funnel imports
algebraic-dev Sep 21, 2025
3acafdc
fix: imports
algebraic-dev Sep 21, 2025
a6699f6
feat: tests and small changes
algebraic-dev Sep 23, 2025
9f4f209
fix: bytestream
algebraic-dev Sep 25, 2025
299153f
fix: remove useless coe
algebraic-dev Sep 25, 2025
8425f4a
feat: add header value validation
algebraic-dev Sep 25, 2025
9de7f6a
fix: merge
algebraic-dev Sep 25, 2025
3ceda32
fix: test and small comments
algebraic-dev Sep 25, 2025
5b86613
feat: small changes
algebraic-dev Sep 26, 2025
c40bdd6
fix: url parser
algebraic-dev Sep 26, 2025
1ce758e
fix: comment and bytebuffer
algebraic-dev Sep 26, 2025
182c8f4
fix: comments
algebraic-dev Sep 26, 2025
3313ece
fix: bytestream comments
algebraic-dev Sep 26, 2025
f39c893
fix: comments
algebraic-dev Sep 26, 2025
7b777a5
fix: style changes
algebraic-dev Sep 26, 2025
5f1f4eb
feat: improve comment of serveConnection
algebraic-dev Sep 26, 2025
b32fc39
fix: small changes
algebraic-dev Sep 26, 2025
2bf41e9
feat: small changes
algebraic-dev Oct 3, 2025
4305f5f
fix: update to master
algebraic-dev Oct 7, 2025
a886a2d
fix: timeout
algebraic-dev Oct 7, 2025
4ef2f03
fix: test
algebraic-dev Oct 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Std/Internal.lean
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ prelude
public import Std.Internal.Async
public import Std.Internal.Parsec
public import Std.Internal.UV
public import Std.Internal.Http

@[expose] public section

Expand Down
14 changes: 14 additions & 0 deletions src/Std/Internal/Async/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,13 @@ Construct an `ETask` that is already resolved with value `x`.
protected def pure (x : α) : ETask ε α :=
Task.pure <| .ok x

/--
Returns `true` if a `ETask` has finished it's execution
-/
@[inline]
protected def isFinished (x : ETask ε α) : BaseIO Bool := do
return (← IO.getTaskState x) == IO.TaskState.finished

/--
Creates a new `ETask` that will run after `x` has finished. If `x`:
- errors, return an `ETask` that resolves to the error.
Expand Down Expand Up @@ -242,6 +249,13 @@ Construct an `AsyncTask` that is already resolved with value `x`.
protected def pure (x : α) : AsyncTask α :=
Task.pure <| .ok x

/--
Returns `true` if a `AsyncTask` has finished it's execution
-/
@[inline]
protected def isFinished (x : AsyncTask α) : BaseIO Bool := do
return (← IO.getTaskState x) == IO.TaskState.finished

/--
Create a new `AsyncTask` that will run after `x` has finished.
If `x`:
Expand Down
65 changes: 65 additions & 0 deletions src/Std/Internal/Http.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sofia Rodrigues
-/
module

prelude
public import Std.Internal.Http.Data
public import Std.Internal.Http.Server

public section

namespace Std
namespace Http

set_option linter.all true

/-!
# Http

A "low-level" HTTP 1.1 implementation for Lean. It is designed to be used with or without the
`Async` library if you want to implement a custom `Connection`.

# Overview

This module of the standard library defines many concepts related to the HTTP protocol
and its semantics in a Sans-IO format. The main function of this library is `Std.Http.Server.serve`,
located in the module `Std.Internal.Http.Server`. It starts a simple HTTP/1.1 server that
handles all requests and sends them to a simple handler function. It uses the default `Std.Internal.Async`
library, but it can be customized to use whatever IO library you want, as the protocol implementation
is pure.

If you want to customize how your server handles sockets, you can use `Std.Http.Server.serveConnection`,
which is a simple function to bind a handler to a `ClientConnection`.

# Low-Level Protocol Implementation

This library provides a low-level foundation that allows you to implement your own IO layer on top
of it. The core protocol parsing and generation logic is available in `Std.Internal.Http.Protocol`,
which provides pure functions for HTTP message parsing and serialization. This design allows you to
integrate the HTTP protocol handling with any IO system or networking library of your choice, while
reusing the robust protocol implementation.

# Minimal Example

```lean
import Std.Internal.Http
import Std.Internal.Async

open Std.Internal.IO.Async
open Std Http

def handler (req : Request Body) : Async (Response Body) := do
let some data ← req.body.collectString
| return Response.badRequest "expected a utf8 body"

return Response.ok ("hi, " ++ data)

def mainAsync : Async Unit := do
Server.serve (.v4 (.mk (.ofParts 0 0 0 0) 8080)) handler

def main := mainAsync.block
```
-/
17 changes: 17 additions & 0 deletions src/Std/Internal/Http/Data.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sofia Rodrigues
-/
module

prelude
public import Std.Internal.Http.Data.Body
public import Std.Internal.Http.Data.Headers
public import Std.Internal.Http.Data.Method
public import Std.Internal.Http.Data.Version
public import Std.Internal.Http.Data.Request
public import Std.Internal.Http.Data.Response
public import Std.Internal.Http.Data.URI
public import Std.Internal.Http.Data.Status
public import Std.Internal.Http.Data.Version
116 changes: 116 additions & 0 deletions src/Std/Internal/Http/Data/Body.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sofia Rodrigues
-/
module

prelude
public import Std.Internal.Http.Data.Body.Length
public import Std.Internal.Http.Data.Body.ByteStream

public section

open Std Internal IO Async

namespace Std
namespace Http

set_option linter.all true

/--
Type that represents the body of a request or response with streams of bytearrays or bytearrays of fixed
size.
-/
inductive Body where
/--
Empty body with no content
-/
| zero

/--
Body containing raw byte data stored in memory
-/
| bytes (data : ByteArray)

/--
Body containing streaming data from a byte stream channel
-/
| stream (channel : Body.ByteStream)
deriving Inhabited

namespace Body

/--
Get content length of a body (if known).
-/
def getContentLength (body : Body) : Length :=
match body with
| zero => .fixed 0
| .bytes data => .fixed data.size
| .stream _ => .chunked

/--
Close the body and release any associated resources. For streaming bodies, this closes the underlying channel.
For other body types, this is a no-op.
-/
def close (body : Body) : Async Unit :=
match body with
| .stream channel => channel.close
| _ => pure ()

instance : Coe String Body where
coe := .bytes ∘ String.toUTF8

instance : Coe ByteArray Body where
coe := .bytes

instance : Coe Body.ByteStream Body where
coe := .stream

/--
Iterate over the body content in chunks, processing each ByteArray chunk with the given step function.
This allows for memory-efficient processing of large bodies without loading everything into memory at once.
-/
@[inline]
protected partial def forIn
{β : Type} (body : Body) (acc : β)
(step : ByteArray → β → Async (ForInStep β)) :
Async β := do
let rec @[specialize] loop (stream : ByteStream) (acc : β) : Async β := do
if let some data ← stream.recv then
match ← step data acc with
| .done res => pure res
| .yield res => loop stream res
else
return acc

match body with
| .zero => pure acc
| .bytes data =>
match ← step data acc with
| .done x => pure x
| .yield x => pure x
| .stream strea => loop strea acc

instance : ForIn Async Body ByteArray where
forIn := Body.forIn

/--
Collect all data from the body into a single `ByteArray`. This reads the entire body content into memory,
so use with caution for large bodies as it may consume significant memory.
-/
def collectByteArray (body : Body) : Async ByteArray := do
let mut result := .empty
for x in body do result := result ++ x
return result

/--
Collect all data from the body into a single `String`. This reads the entire body content into memory,
so use with caution for large bodies as it may consume significant memory. If it's a valid UTF8 string
then it will return `some` otherwise `none`.
-/
def collectString (body : Body) : Async (Option String) := do
let mut result := .empty
for x in body do result := result ++ x
return String.fromUTF8? result
Loading
Loading