Add SSE transport + Built with eth.zig section#48
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdded a Server‑Sent Events (SSE) transport implementation and exports, updated README, and added reconnect/backoff options to the WebSocket transport. New SSE parser, subscription APIs (with reconnect), tests, and a ws reconnect helper were introduced. Changes
Sequence Diagram(s)sequenceDiagram
participant Caller as Caller
participant Sub as subscribe()
participant HTTP as HTTP Client
participant Server as SSE Server
participant Parser as SseParser
participant CB as Callback
Caller->>Sub: subscribe(allocator, url, extra_headers, parser, callback)
Sub->>HTTP: Prepare headers (Accept: text/event-stream, Cache-Control: no-cache, Last-Event-ID?)
Sub->>HTTP: GET request to url
HTTP->>Server: Send request
Server-->>HTTP: 200 OK + stream
loop For each line in stream
HTTP-->>Sub: Read line
Sub->>Parser: feedLine(line)
alt Event boundary (empty line)
Parser-->>Sub: ?SseEvent
Sub->>CB: callback(event)
else Accumulating
Parser-->>Sub: null
end
end
HTTP-->>Sub: Connection closed / error
Sub-->>Caller: return or error
sequenceDiagram
participant Caller as Caller
participant Reconnect as subscribeWithReconnect()
participant Sub as subscribe()
participant Backoff as Backoff Handler
Caller->>Reconnect: subscribeWithReconnect(allocator, url, headers, opts, callback)
loop Infinite reconnection loop
Reconnect->>Sub: subscribe(...)
alt Connection succeeds (clean close)
Sub-->>Reconnect: stream closed normally
Reconnect->>Reconnect: reset backoff to initial_backoff_ms
else Connection fails / disconnected
Sub-->>Reconnect: error / disconnected
end
Reconnect->>Backoff: on_reconnect(current_backoff_ms) [if set]
Reconnect->>Backoff: sleep current_backoff_ms
Reconnect->>Reconnect: double backoff (capped by max_backoff_ms or server retry)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@README.md`:
- Around line 135-139: The fenced code block containing the tree snippet (lines
showing "eth.zig", "perpcity-zig-sdk", "gator-liquidators") needs a language tag
to satisfy MD040; update the triple-backtick opening to include a tag such as
text (e.g., change ``` to ```text) so the block is explicitly fenced with a
language identifier.
In `@src/sse_transport.zig`:
- Around line 69-71: The handler for SSE `data` lines currently overwrites
self.current_data instead of appending; change the logic where you check if
field == "data" (the block that sets self.current_data = value and the similar
block around lines 271-278) to append newline-separated values: if
self.current_data is empty set it to value, otherwise set it to
self.current_data ++ "\n" ++ value (or the Zig equivalent for concatenation), so
multiple `data:` lines are joined with '\n' into a single event payload.
- Line 72: The SSE implementation currently ignores the "id" and "retry" fields
and reconnects without sending Last-Event-ID or adjusting the backoff; update
the SSE parser (the code that parses event fields such as "id" and "retry") to
store the last seen event id and the server-specified retry value, include
Last-Event-ID in the HTTP reconnect header when present, and use the parsed
retry value to control reconnect delay in the reconnect loop (where
disconnection/reconnect is handled). Ensure the stored last-event-id is
persisted across reconnect attempts and that the reconnect/backoff code consults
the server-provided retry before falling back to a client default.
- Around line 10-15: The SseEvent struct is missing the required id field and
its fields don't match the stated contract (id, event, data); update the struct
SseEvent to include an optional id field (e.g., id: ?[]const u8 = null) and
align field names to the contract (use event instead of event_type and keep
data: ?[]const u8 = null), then update any code that constructs or reads
SseEvent to use the new id and event names so SSE can be handled losslessly and
support reconnect continuity.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e637759e-a156-4b88-b448-06a2164ccd5d
📒 Files selected for processing (3)
README.mdsrc/root.zigsrc/sse_transport.zig
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/sse_transport.zig`:
- Around line 76-83: The SSE parser currently emits callbacks for frames that
contain only `id:` or `event:` and only updates `last_event_id` when `id_len >
0`, which prevents `id:` from clearing the last ID and causes spurious
dispatched events; modify the logic in the parsing function that checks
`self.event_len`, `self.data_len`, and `self.id_len` so that you do NOT dispatch
any callbacks when `self.data_len == 0` (i.e., non-data frames should not be
emitted), but still apply the `id:` update unconditionally (update
`self.last_event_id_buf`/`self.last_event_id_len` even when `self.id_len == 0`
to clear the prior ID); apply the same change in the corresponding code path
around the other occurrence (the block referenced at lines ~124-127) to ensure
consistent SSE resume semantics.
- Around line 103-135: The parser currently ignores lines without a colon
because it only executes the "field:value" branch when std.mem.indexOf finds a
colon; change the logic so lines lacking ":" are treated as a valid field with
an empty value (i.e., set field = trimmed and value = an empty slice) before the
existing handling; update the colon-trimming and optional-leading-space handling
to only strip a leading space when value.len > 0, and keep the rest of the
existing branches that copy into self.event_buf/self.data_buf/self.id_buf or
parse self.retry_ms unchanged so lines like "data" are preserved and appended
correctly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 83e777b0-5623-45be-84df-1e4cd7091d1f
📒 Files selected for processing (2)
README.mdsrc/sse_transport.zig
🚧 Files skipped from review as they are similar to previous changes (1)
- README.md
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
src/sse_transport.zig (4)
194-195: Remove dead code.
prefixis declared and immediately suppressed with_ = prefix. This appears to be leftover from refactoring.🧹 Proposed fix
if (parser.lastEventId()) |last_id| { - const prefix = ""; // value is the id itself; name is the header name - _ = prefix; `@memcpy`(last_id_header_buf[0..last_id.len], last_id);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/sse_transport.zig` around lines 194 - 195, The local constant prefix and its suppression (_ = prefix) are dead code; remove the declaration "const prefix = \"\";" and the suppression line "_ = prefix;" from sse_transport.zig (around the code that sets the header name/id) so there is no unused variable; ensure no other code relies on the symbol prefix before committing.
22-25:ConnectionFailederror variant appears unused.
subscribe()returnserror.BadStatusfor non-OK responses but propagates underlying connection errors directly rather than wrapping them asConnectionFailed. Consider either using this variant or removing it to avoid confusion.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/sse_transport.zig` around lines 22 - 25, SseError.ConnectionFailed is defined but never used; update the subscribe() implementation to wrap underlying connection errors as error.SseError.ConnectionFailed (preserving original error info) instead of propagating raw errors, or remove the ConnectionFailed variant from the SseError declaration and adjust any callers expecting that variant; look for the SseError type and the subscribe() function to apply the change and ensure error.BadStatus remains used for non-OK HTTP responses.
46-56: Silent truncation when event data exceeds 64KB buffer.If SSE data exceeds
data_buf.len(65536 bytes), lines 128-131 silently truncate without any indication to the caller. For MEV-Share events this is likely sufficient, but consider either documenting this limit prominently or returning an error/flag when truncation occurs.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/sse_transport.zig` around lines 46 - 56, The SSE parser currently silently truncates event payloads when data_buf (65536) is exceeded; update the parsing logic that writes into data_buf/data_len (and any code paths using has_id/event_buf/id_buf) to detect buffer overflow and signal it instead of truncating silently: add a boolean flag (e.g., data_truncated) or return an explicit error from the parse/consume function when data_len would exceed data_buf.len, set that flag/return the error at the point of overflow detection, and propagate/handle it in callers so callers can log/handle oversized events or fallback to a documented behavior; ensure the new flag/error is initialized/cleared when starting a new event block alongside has_id and data_len.
286-288: Potential integer overflow ifinitial_backoff_msis very large.
backoff_ms * 2is computed before@min, so if a caller provides aninitial_backoff_msnearu64max, multiplication overflows before clamping. With defaults this is safe, but consider defensive ordering:🛡️ Safer ordering
if (parser.retry_ms == null) { - backoff_ms = `@min`(backoff_ms * 2, opts.max_backoff_ms); + backoff_ms = if (backoff_ms > opts.max_backoff_ms / 2) + opts.max_backoff_ms + else + backoff_ms * 2; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/sse_transport.zig` around lines 286 - 288, The multiplication backoff_ms * 2 can overflow before `@min` clamps it; change the logic in the block that checks parser.retry_ms to avoid multiplying first: compute a safe doubled value by comparing backoff_ms to opts.max_backoff_ms/2 (or checking against the u64 half) and if backoff_ms > opts.max_backoff_ms/2 set backoff_ms = opts.max_backoff_ms else set backoff_ms = backoff_ms * 2; reference variables: parser.retry_ms, backoff_ms, opts.max_backoff_ms.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/sse_transport.zig`:
- Around line 368-374: The test "SseParser handles event with no data" is wrong:
per SSE spec SseParser.feedLine returns null when no data is present; update the
test to expect null instead of unwrapping—modify the assertions in the test to
assert parser.feedLine("") == null (or use std.testing.expectNull) and remove
the subsequent unwraps/expectEqualStrings/expect for evt.data; this keeps
SseParser and feedLine unchanged.
- Around line 173-239: The subscribe function's streaming read loop can block
indefinitely because reader.takeDelimiterInclusive is called in a tight
while(true) with no timeout; update subscribe to guard streaming reads with a
read timeout (e.g., use std.posix.select or a socket deadline wrapper) around
calls to response.reader()/reader.takeDelimiterInclusive so a stalled connection
returns a distinct error (or closes/retries) instead of hanging; ensure you
reference the same symbols (subscribe, client.request/.sendBodiless,
response.reader, reader.takeDelimiterInclusive) and either accept a timeout
parameter or use a configured constant, and propagate/handle the timeout error
where the current EndOfStream/err switch is done.
---
Nitpick comments:
In `@src/sse_transport.zig`:
- Around line 194-195: The local constant prefix and its suppression (_ =
prefix) are dead code; remove the declaration "const prefix = \"\";" and the
suppression line "_ = prefix;" from sse_transport.zig (around the code that sets
the header name/id) so there is no unused variable; ensure no other code relies
on the symbol prefix before committing.
- Around line 22-25: SseError.ConnectionFailed is defined but never used; update
the subscribe() implementation to wrap underlying connection errors as
error.SseError.ConnectionFailed (preserving original error info) instead of
propagating raw errors, or remove the ConnectionFailed variant from the SseError
declaration and adjust any callers expecting that variant; look for the SseError
type and the subscribe() function to apply the change and ensure error.BadStatus
remains used for non-OK HTTP responses.
- Around line 46-56: The SSE parser currently silently truncates event payloads
when data_buf (65536) is exceeded; update the parsing logic that writes into
data_buf/data_len (and any code paths using has_id/event_buf/id_buf) to detect
buffer overflow and signal it instead of truncating silently: add a boolean flag
(e.g., data_truncated) or return an explicit error from the parse/consume
function when data_len would exceed data_buf.len, set that flag/return the error
at the point of overflow detection, and propagate/handle it in callers so
callers can log/handle oversized events or fallback to a documented behavior;
ensure the new flag/error is initialized/cleared when starting a new event block
alongside has_id and data_len.
- Around line 286-288: The multiplication backoff_ms * 2 can overflow before
`@min` clamps it; change the logic in the block that checks parser.retry_ms to
avoid multiplying first: compute a safe doubled value by comparing backoff_ms to
opts.max_backoff_ms/2 (or checking against the u64 half) and if backoff_ms >
opts.max_backoff_ms/2 set backoff_ms = opts.max_backoff_ms else set backoff_ms =
backoff_ms * 2; reference variables: parser.retry_ms, backoff_ms,
opts.max_backoff_ms.
| pub fn subscribe( | ||
| allocator: std.mem.Allocator, | ||
| url: []const u8, | ||
| extra_headers: []const std.http.Header, | ||
| parser: *SseParser, | ||
| callback: *const fn (event: SseEvent) void, | ||
| ) !void { | ||
| var client = std.http.Client{ .allocator = allocator }; | ||
| defer client.deinit(); | ||
|
|
||
| const uri = try std.Uri.parse(url); | ||
|
|
||
| // Build header list: base SSE headers + Last-Event-ID (if any) + caller extras. | ||
| const base_headers: []const std.http.Header = &.{ | ||
| .{ .name = "Accept", .value = "text/event-stream" }, | ||
| .{ .name = "Cache-Control", .value = "no-cache" }, | ||
| }; | ||
|
|
||
| var last_id_header_buf: [512 + 20]u8 = undefined; // "Last-Event-ID: " + id | ||
| var id_headers: []const std.http.Header = &.{}; | ||
| if (parser.lastEventId()) |last_id| { | ||
| const prefix = ""; // value is the id itself; name is the header name | ||
| _ = prefix; | ||
| @memcpy(last_id_header_buf[0..last_id.len], last_id); | ||
| id_headers = &.{.{ | ||
| .name = "Last-Event-ID", | ||
| .value = last_id_header_buf[0..last_id.len], | ||
| }}; | ||
| } | ||
|
|
||
| const all_headers = try std.mem.concat( | ||
| allocator, | ||
| std.http.Header, | ||
| &.{ base_headers, id_headers, extra_headers }, | ||
| ); | ||
| defer allocator.free(all_headers); | ||
|
|
||
| var req = try client.request(.GET, uri, .{ .extra_headers = all_headers }); | ||
| defer req.deinit(); | ||
|
|
||
| try req.sendBodiless(); | ||
|
|
||
| var redirect_buf: [4096]u8 = undefined; | ||
| var response = try req.receiveHead(&redirect_buf); | ||
|
|
||
| if (response.head.status != .ok) { | ||
| return error.BadStatus; | ||
| } | ||
|
|
||
| // Reset per-event state but preserve last_event_id and retry_ms. | ||
| parser.reset(); | ||
|
|
||
| var transfer_buf: [8192]u8 = undefined; | ||
| const reader = response.reader(&transfer_buf); | ||
|
|
||
| while (true) { | ||
| const line_with_nl = reader.takeDelimiterInclusive('\n') catch |err| switch (err) { | ||
| error.EndOfStream => return, // normal close | ||
| else => return err, | ||
| }; | ||
| const line = line_with_nl[0 .. line_with_nl.len - 1]; | ||
|
|
||
| if (parser.feedLine(line)) |evt| { | ||
| callback(evt); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check Zig version in build.zig or any version constraints
fd -t f 'build.zig' --exec cat {} \; 2>/dev/null | head -50
# Search for any existing timeout handling or std.http usage patterns
rg -n 'std\.http' --type zig -C 3Repository: StrobeLabs/eth.zig
Length of output: 4193
🌐 Web query:
Zig std.http.Client API request sendBodiless receiveHead reader 0.12 0.13
💡 Result:
In Zig 0.12.x / 0.13.x, std.http.Client.Request does not use the newer “split” API (sendBodiless, receiveHead). Instead, the flow is:
req.send()- (optional)
req.writeAll(payload) req.finish()req.wait()- read body via
req.reader()(afterwait) (raw.githubusercontent.com)
That’s why code/examples mentioning sendBodiless / receiveHead won’t match 0.12/0.13—they correspond to the newer request API where you can explicitly do sendBodiless() and then receiveHead(redirect_buffer) (git.medv.io).
0.12 / 0.13 minimal GET example (no request body)
var req = try client.open(.GET, uri, .{
.server_header_buffer = &server_header_buf,
});
defer req.deinit();
try req.send();
try req.finish();
try req.wait();
var r = req.reader();
const n = try r.readAll(&body_buf);(For POST/PUT: set the request transfer info as needed, send(), writeAll(payload), finish(), wait(), then read with reader().) (raw.githubusercontent.com)
Citations:
- 1: https://raw.githubusercontent.com/ziglang/zig/0.12.0/lib/std/http/Client.zig
- 2: https://git.medv.io/zig/blob/master/lib/std/http/Client.zig.html?utm_source=openai
- 3: https://raw.githubusercontent.com/ziglang/zig/0.12.0/lib/std/http/Client.zig
🏁 Script executed:
# View the http_transport.zig file to see actual std.http.Client usage
wc -l src/http_transport.zig && echo "---" && sed -n '1,100p' src/http_transport.zig
# Search for timeout-related code or patterns
rg -i 'timeout|deadline|sleep|std\.time' --type zig -B 2 -A 2Repository: StrobeLabs/eth.zig
Length of output: 5757
🏁 Script executed:
# Check if response.reader() or similar patterns are used elsewhere
rg -n 'response\.reader|takeDelimiter|receiveHead|sendBodiless' --type zig -B 2 -A 2Repository: StrobeLabs/eth.zig
Length of output: 970
Consider adding timeout protection to the SSE stream reader.
The HTTP Client API calls (sendBodiless(), receiveHead(), response.reader(), takeDelimiterInclusive()) are compatible with the target Zig 0.15.2. However, the while (true) loop at line 228 has no timeout on stream reads. If the server hangs or the network partition persists, this will block indefinitely. For production MEV-Share usage, consider adding a read timeout (e.g., via std.posix.select or a wrapper) or documenting this limitation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/sse_transport.zig` around lines 173 - 239, The subscribe function's
streaming read loop can block indefinitely because reader.takeDelimiterInclusive
is called in a tight while(true) with no timeout; update subscribe to guard
streaming reads with a read timeout (e.g., use std.posix.select or a socket
deadline wrapper) around calls to
response.reader()/reader.takeDelimiterInclusive so a stalled connection returns
a distinct error (or closes/retries) instead of hanging; ensure you reference
the same symbols (subscribe, client.request/.sendBodiless, response.reader,
reader.takeDelimiterInclusive) and either accept a timeout parameter or use a
configured constant, and propagate/handle the timeout error where the current
EndOfStream/err switch is done.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/sse_transport.zig (1)
12-20:⚠️ Potential issue | 🟠 MajorAlign
SseEvent.datawith the promised public API shape.
SseEvent.datais optional at Line 19, but the issue objective defines it as required (data: []const u8). This creates avoidable nullable handling at call sites and diverges from the expected contract.Suggested API alignment
pub const SseEvent = struct { @@ - data: ?[]const u8 = null, + data: []const u8 = "", };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/sse_transport.zig` around lines 12 - 20, SseEvent.data is declared nullable but the public API promises a non-nullable array; change the field in the SseEvent struct from "data: ?[]const u8 = null" to a required "data: []const u8" and update any constructors/initializations that create SseEvent (search for uses of SseEvent literal or field assignments) to supply an empty slice (e.g., "") or the appropriate data slice so callers no longer need nullable handling; ensure any code that merged multiple data lines still produces a []const u8 result before constructing or assigning to SseEvent.data.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/sse_transport.zig`:
- Around line 93-97: The code currently gates dispatching an SseEvent on
self.data_len > 0 which drops valid events that have an explicitly empty data:
line; change the logic to track presence of any data: field instead of byte
length (e.g., add or reuse a boolean like self.has_data or set a flag when
parsing a data: line), set that flag when a data: line is seen even if zero
bytes, and use that flag in the creation of evt (SseEvent .data should be
self.data_buf[0..self.data_len] which may be an empty slice) so empty-data
events are dispatched; update parsing code paths that previously only
incremented data_len (where data lines are handled) to set the presence flag as
well and replace checks of self.data_len > 0 with the presence flag when
deciding to construct evt.
- Around line 285-288: The code multiplies delay by std.time.ns_per_ms which can
overflow for very large parser.retry_ms/backoff_ms; update the reconnect path in
src/sse_transport.zig to perform an overflow-safe conversion before calling
std.Thread.sleep: compute ns_per_ms = std.time.ns_per_ms and use a checked
multiplication (e.g., std.math.mulWithOverflow or explicit bound check against
(`@as`(u64).max / ns_per_ms)) to produce a capped or error-handled ns_delay, call
opts.on_reconnect with the original delay value as before, and pass the safe
ns_delay to std.Thread.sleep; reference symbols: delay, parser.retry_ms,
backoff_ms, opts.on_reconnect, std.time.ns_per_ms, std.Thread.sleep.
---
Duplicate comments:
In `@src/sse_transport.zig`:
- Around line 12-20: SseEvent.data is declared nullable but the public API
promises a non-nullable array; change the field in the SseEvent struct from
"data: ?[]const u8 = null" to a required "data: []const u8" and update any
constructors/initializations that create SseEvent (search for uses of SseEvent
literal or field assignments) to supply an empty slice (e.g., "") or the
appropriate data slice so callers no longer need nullable handling; ensure any
code that merged multiple data lines still produces a []const u8 result before
constructing or assigning to SseEvent.data.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/ws_transport.zig`:
- Around line 681-684: The backoff is only reset when callback(&transport)
returns success, which leaves backoff high if the session later errors; change
the logic to reset backoff when a connection/session is successfully established
rather than only on a clean callback return: move or add the assignment
backoff_ms = opts.initial_backoff_ms to the code path immediately after the
successful connection/setup and before invoking callback(&transport) (or
immediately after confirming the transport became healthy), referencing the
existing symbols callback(&transport), backoff_ms, and opts.initial_backoff_ms;
apply the same change across the surrounding block covering the callback/session
loop (lines ~677-695) so any successful session establishment clears the
backoff.
…Event-ID on reconnect
…no-colon field support
…ted flag, overflow-safe backoff, fix no-data test
…ta, overflow-safe sleep
ce9ac9a to
12e697e
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/sse_transport.zig`:
- Around line 455-458: The test attempts to optional-unwrap ev2.data but
SseEvent.data is a non-optional []const u8; remove the optional unwrap and
compare directly (use ev2.data instead of ev2.data.?) in the assertions that
follow parser.feedLine("") so the code compiles; locate the test lines
referencing parser.feedLine, ev2.event and ev2.data and update the
expectEqualStrings call to pass ev2.data as a non-optional value.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7194fa08-6e67-4b97-ba13-8c7c0f0f87e0
📒 Files selected for processing (4)
README.mdsrc/root.zigsrc/sse_transport.zigsrc/ws_transport.zig
🚧 Files skipped from review as they are similar to previous changes (1)
- src/root.zig
Summary
src/sse_transport.zig— a spec-compliant Server-Sent Events transport, upstreamed from production use in gator-liquidatorseth.sse_transportfromroot.zigSSE Transport
The
SseParserandsubscribe/subscribeWithReconnectfunctions were extracted from gator-liquidators'sse_client.zigwhere they've been running in production. The app-specific wake logic stays in gator-liquidators; the general parsing and connection handling lives here.Closes #33.
Test plan
zig build testpasses (all 13 SSE parser tests included)eth.sse_transport.SseParser,SseEvent,subscribe,subscribeWithReconnectare accessible via@import("eth")Summary by CodeRabbit
New Features
Documentation
Tests