view seobeo/README.md @ 257:609d3c6aff4e

[seobeo] Add persistent SSE streams Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Tue, 04 Aug 2026 16:49:11 -0700
parents 745fd127b2a1
children
line wrap: on
line source

# seobeo

HTTP client and networking library for C.

## Features

- HTTP/HTTPS client
- SSL/TLS support
- Async networking with libuv
- Joinable/detached tasks and bounded worker pools
- Server-Sent Events routes and event framing
- Snapshot testing utilities

## Files

| File | Description |
|------|-------------|
| `seobeo.h` | Public API header |
| `seobeo_internal.h` | Internal declarations |
| `s_http_client.c` | HTTP client implementation |
| `s_network.c` | Network utilities |
| `s_sse.c` | Server-Sent Events framing and stream lifecycle |
| `s_ssl.c` | SSL/TLS handling |
| `s_logging.c` | Logging utilities |
| `s_worker.c` | Thread and worker-pool implementation |
| `seobeo_worker.h` | Public worker API |
| `snapshot_creator.c/h` | Snapshot testing |
| `docs/` | Documentation |
| `examples/` | Usage examples |
| `tests/` | Unit tests |
| `os/` | OS-specific code |

## Usage

```c
#include "seobeo/seobeo.h"

// Make HTTP request
HttpResponse* resp = http_get("https://example.com");
// handle response...
http_response_free(resp);
```

## Building

```bash
bazel build //seobeo:seobeo
bazel test //seobeo:seobeo_test
```

## Background workers

Use a detached task for simple fire-and-forget work:

```c
void Convert_Image(void *p_context)
{
  Conversion *p_conversion = p_context;
  Run_FFmpeg(p_conversion);
}

Seobeo_Worker_Result result =
    Seobeo_Thread_Start_Detached(
        Convert_Image,
        p_conversion,
        free);
if (result != SEOBEO_WORKER_OK)
  free(p_conversion);
```

Use a bounded pool when requests can enqueue expensive work:

```c
Seobeo_Worker_Pool *p_pool =
    Seobeo_Worker_Pool_Create(2, 16);

Seobeo_Worker_Result result =
    Seobeo_Worker_Pool_Submit(
        p_pool,
        Convert_Image,
        p_conversion,
        free);

Seobeo_Worker_Pool_Shutdown(p_pool, TRUE);
Seobeo_Worker_Pool_Destroy(p_pool);
```

Submitting transfers context ownership only when it returns
`SEOBEO_WORKER_OK`. Cleanup runs after successful work and for queued tasks
discarded by a non-draining shutdown. User callbacks never run while the pool
mutex is held.

## Server-Sent Events

Register an SSE route separately from ordinary response and stream handlers:

```c
static void Build_Events(
    Seobeo_SSE_Stream *p_stream,
    Seobeo_Request_Entry *p_request,
    Dowa_Arena *p_arena)
{
  (void)p_request;
  (void)p_arena;
  Seobeo_SSE_Event event = {
    .event = "build",
    .id = "42",
    .data = "started\ncompiling",
    .retry_ms = 2000,
  };
  Seobeo_SSE_Send(p_stream, &event);
  Seobeo_SSE_Send_Comment(p_stream, "heartbeat");
}

Seobeo_Router_Register_SSE("/events/builds", Build_Events);
```

SSE handlers run once when a client connects and must return promptly so edge
workers remain available. Call `Seobeo_SSE_Retain` before storing a stream for
later work and pair it with `Seobeo_SSE_Release`; use
`Seobeo_SSE_Is_Open` before sending. Do not retain the request map or arena
after the handler returns. A return value of `1` means the record was retained
behind socket backpressure and `Seobeo_SSE_Flush` should be retried later.
`SEOBEO_SSE_BACKPRESSURE` means the bounded pending queue is full and the new
record was not accepted. Events are bounded by the handle write-buffer
capacity so one record is queued atomically; larger records return
`SEOBEO_SSE_EVENT_TOO_LARGE`.

## Dependencies

- libuv (via //third_party/libuv)
- OpenSSL