From 0f4d4ae76105075507215d846b66963972a2bc5a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 7 Oct 2026 13:14:56 +0000 Subject: [PATCH 1/3] build: pin bend to the commit that packages 2.0.36 inputs.bend is eebc18cd04daeade06c3f68c3c96c1faefd3462f, the commit whose flake names 2.0.36. ez's bend follows this input, and the README states the build is checked on 2.0.36. Co-authored-by: noah-emp --- README.md | 2 +- flake.lock | 8 ++++---- flake.nix | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c0d3386..6b43b3b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ HTTP client and server for [Bend 2](https://github.com/bendlang/bend). ## Install It needs bend 2.0.32 or later (the server binds `TCP.listen(host, port)`). -ezhttp is built and checked on bend 2.0.35. +ezhttp is built and checked on bend 2.0.36. With [Bend](https://github.com/bendlang/bend) alone there is no install step: import ezhttp by its hub name and `bend` fetches it from diff --git a/flake.lock b/flake.lock index 3e1f5ec..4d2e084 100644 --- a/flake.lock +++ b/flake.lock @@ -7,17 +7,17 @@ ] }, "locked": { - "lastModified": 1791027223, - "narHash": "sha256-VsMHe5HUg9EC+l8PlfOu2IHTVmP5Vu04GPDYwSkm6q8=", + "lastModified": 1791348820, + "narHash": "sha256-2YQX/3rN/NVkKcxjVMPLs4D5ItBwsVTEJxxKV6mIGSE=", "owner": "bendlang", "repo": "bend", - "rev": "5a0b523f7759335164f1dead0e0815234a5fd9dc", + "rev": "eebc18cd04daeade06c3f68c3c96c1faefd3462f", "type": "github" }, "original": { "owner": "bendlang", "repo": "bend", - "rev": "5a0b523f7759335164f1dead0e0815234a5fd9dc", + "rev": "eebc18cd04daeade06c3f68c3c96c1faefd3462f", "type": "github" } }, diff --git a/flake.nix b/flake.nix index d284396..b3c8209 100644 --- a/flake.nix +++ b/flake.nix @@ -2,14 +2,14 @@ description = "ezhttp: HTTP client and server for Bend 2"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - # bendlang/bend's flake at the commit that packages 2.0.35 (the v2.0.35 tag - # still packages 2.0.34) + # bendlang/bend's flake at the commit that packages 2.0.36 (the v2.0.36 tag + # still packages 2.0.35) inputs.bend = { - url = "github:bendlang/bend/5a0b523f7759335164f1dead0e0815234a5fd9dc"; + url = "github:bendlang/bend/eebc18cd04daeade06c3f68c3c96c1faefd3462f"; inputs.nixpkgs.follows = "nixpkgs"; }; # ez 1.3.0's bend follows this flake's bend: - # ez, `ez prove` and bolt all build on 2.0.35. + # ez, `ez prove` and bolt all build on 2.0.36. inputs.ez = { url = "github:Emerging-Patterns/ez"; inputs.nixpkgs.follows = "nixpkgs"; From 7032296483c7f76c4b4712934aa00fc57b951614 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 7 Oct 2026 13:25:06 +0000 Subject: [PATCH 2/3] fix: Bend 2.0.36 recv, send, and effect registration TCP.recv returns Maybe: None when the peer closes its write side. TCP.send's failure pairs the kernel error with the unsent tail. io_eff takes the constructor id and the runner. Co-authored-by: noah-emp --- src/effs/wire.c | 2 +- src/server.bend | 41 ++++++++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/effs/wire.c b/src/effs/wire.c index 884ff78..e396881 100644 --- a/src/effs/wire.c +++ b/src/effs/wire.c @@ -338,5 +338,5 @@ Term ezwire_talk_run(Env e, Term* f, IoWork* w) { } static void __attribute__((constructor)) ezwire_talk_use(void) { - io_eff(CID(ezwire.talk), ezwire_talk_run, 0); + io_eff(CID(ezwire.talk), ezwire_talk_run); } diff --git a/src/server.bend b/src/server.bend index 8ab6e90..432b835 100644 --- a/src/server.bend +++ b/src/server.bend @@ -105,21 +105,35 @@ def server.read.use( ) -> IO(Socket & Http.Request): server.read.empty(sock, got, chunk, rest, String.eq(chunk, "")) +# None{} is the peer closing its write side; Some is the bytes that arrived +def server.read.got( + sock: Socket, + got: String, + rest: Socket -> String -> IO(Socket & Http.Request), + chunk: Maybe<&1, String> +) -> IO(Socket & Http.Request): + match chunk: + case None{}: + server.read.done(sock, got) + case Some{text}: + server.read.use(sock, got, text, rest) + # the bytes from one recv def server.read.pass( sock: Socket, got: String, rest: Socket -> String -> IO(Socket & Http.Request), - r: Result<&1, &1, U32 & String, String> + r: Result<&1, &1, U32 & String, Maybe<&1, String>> ) -> IO(Socket & Http.Request): - IO.bind(String, Socket & Http.Request, IO.pass(String, r), - chunk => server.read.use(sock, got, chunk, rest)) + IO.bind(Maybe<&1, String>, Socket & Http.Request, + IO.pass(Maybe<&1, String>, r), + chunk => server.read.got(sock, got, rest, chunk)) # unpack recv's socket and result def server.read.un( got: String, rest: Socket -> String -> IO(Socket & Http.Request), - pair: Socket & Result<&1, &1, U32 & String, String> + pair: Socket & Result<&1, &1, U32 & String, Maybe<&1, String>> ) -> IO(Socket & Http.Request): (sock, r) = pair server.read.pass(sock, got, rest, r) @@ -132,7 +146,7 @@ def server.read.go(fuel: Nat, sock: Socket, got: String) -> IO(Socket & Http.Req server.read.done(sock, got) case 1n+f: IO.bind( - Socket & Result<&1, &1, U32 & String, String>, + Socket & Result<&1, &1, U32 & String, Maybe<&1, String>>, Socket & Http.Request, TCP.recv(sock, server.max()), pair => server.read.un(got, s => g => server.read.go(f, s, g), pair)) @@ -141,19 +155,28 @@ def server.read.go(fuel: Nat, sock: Socket, got: String) -> IO(Socket & Http.Req def server.read(sock: Socket) -> IO(Socket & Http.Request): server.read.go(server.read.fuel(), sock, "") +# a failed send stops with the kernel's code and message +def server.write.fail(err: (U32 & String) & String) -> IO(Socket): + ((code, message), _left) = err + IO.die(Socket, code, message) + # write a structured response and keep the socket -def server.write.res(sock: Socket, r: Result<&1, &1, U32 & String, Unit>) +def server.write.res(sock: Socket, r: Result<&1, &1, (U32 & String) & String, Unit>) -> IO(Socket): - IO.bind(Unit, Socket, IO.pass(Unit, r), _u => IO.pure(Socket, sock)) + match r: + case Done{_u}: + IO.pure(Socket, sock) + case Fail{err}: + server.write.fail(err) -def server.write.un(pair: Socket & Result<&1, &1, U32 & String, Unit>) +def server.write.un(pair: Socket & Result<&1, &1, (U32 & String) & String, Unit>) -> IO(Socket): (s, r) = pair server.write.res(s, r) def server.write(sock: Socket, reply: Http.Reply) -> IO(Socket): IO.bind( - Socket & Result<&1, &1, U32 & String, Unit>, + Socket & Result<&1, &1, (U32 & String) & String, Unit>, Socket, TCP.send(sock, Http.respond.of(reply)), server.write.un) From 51b7fbc71f0196fb532f9c51a8225a908acf1b93 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 7 Oct 2026 13:58:22 +0000 Subject: [PATCH 3/3] build: lock ez and bolt at the 2.0.36 pins inputs.ez is 10276ec0d103f77f14bf2191e6bf7d41416a870f. bolt is efb82d24598bd3e9e64004bc443093d4dd072173. Co-authored-by: noah-emp --- ez.lock.toml | 5 ++--- ez.toml | 5 ++--- flake.lock | 6 +++--- flake.nix | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/ez.lock.toml b/ez.lock.toml index 032c04b..998e483 100644 --- a/ez.lock.toml +++ b/ez.lock.toml @@ -23,8 +23,7 @@ LICENSE = "309f5aae946e4db157750e002fb179a74ed0fe27fce09a7be68a6977b14f205f" [tools] [tools.bolt] git = "https://github.com/Emerging-Patterns/bolt" -rev = "91efe4bbaad91953652794ad55be7b49c10a2eff" -tag = "v1.11.0" +rev = "efb82d24598bd3e9e64004bc443093d4dd072173" root = "." -narHash = "sha256-0xjoxoByI8PKBrbFy4eYGbKQ3ZVscknZLKkWQroYAUI=" +narHash = "sha256-hw4rVnEWEi9Q/Cen7ZGqdz+lYm1vup3+52rBwCEBskY=" entry = "main.bend" diff --git a/ez.toml b/ez.toml index c2ae09b..fd72483 100644 --- a/ez.toml +++ b/ez.toml @@ -13,8 +13,7 @@ entry = "main.bend" [tools] [tools.bolt] git = "https://github.com/Emerging-Patterns/bolt" -rev = "91efe4bbaad91953652794ad55be7b49c10a2eff" -tag = "v1.11.0" +rev = "efb82d24598bd3e9e64004bc443093d4dd072173" root = "." -narHash = "sha256-0xjoxoByI8PKBrbFy4eYGbKQ3ZVscknZLKkWQroYAUI=" +narHash = "sha256-hw4rVnEWEi9Q/Cen7ZGqdz+lYm1vup3+52rBwCEBskY=" entry = "main.bend" diff --git a/flake.lock b/flake.lock index 4d2e084..c0c5aa6 100644 --- a/flake.lock +++ b/flake.lock @@ -31,11 +31,11 @@ ] }, "locked": { - "lastModified": 1790728722, - "narHash": "sha256-6SDNpCeAfR66xuSO8vkoBMZAQ24ZUyGYLc9GFadFYy8=", + "lastModified": 1791381403, + "narHash": "sha256-TaCVkunKx0FZVquSGNHmuzPDAI7DCv8Sy8upvTh324g=", "owner": "Emerging-Patterns", "repo": "ez", - "rev": "b5693ce28337e770b2c70c99d53d311cd044dc24", + "rev": "10276ec0d103f77f14bf2191e6bf7d41416a870f", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index b3c8209..d745261 100644 --- a/flake.nix +++ b/flake.nix @@ -8,7 +8,7 @@ url = "github:bendlang/bend/eebc18cd04daeade06c3f68c3c96c1faefd3462f"; inputs.nixpkgs.follows = "nixpkgs"; }; - # ez 1.3.0's bend follows this flake's bend: + # ez 1.5.0's bend follows this flake's bend: # ez, `ez prove` and bolt all build on 2.0.36. inputs.ez = { url = "github:Emerging-Patterns/ez";