std1.0.0-beta-7 SafeC docs

Source http/sse.h

View in the repository

// SafeC Standard Library — Server-Sent Events (the WHATWG
// text/event-stream format, what the browser's EventSource reads).
//
// Server: a handler takes its connection over with SseStream::start and
// writes events until it is done or the client goes away (send* returns 0):
//
//   struct HttpResponse events(&HttpRequest req, &HttpCtx ctx) {
//       struct SseStream s = std::SseStream::start(ctx.conn, null);
//       while (s.send_event("tick", "{\"n\":1}", null)) { std::thread_sleep_ms(1000UL); }
//       s.close();
//       return std::HttpResponse::new(200);     // ignored: the connection was detached
//   }
//
// The stream blocks the worker thread that runs it; give long-lived
// streams their own thread (or a server with enough workers).
//
// Client: SseClient connects to an http:// or https:// URL and yields parsed events;
// SseParser is the incremental parser underneath, usable on any byte
// source.
#pragma once
#include <std/http/http.h>
#include <std/collections/string.h>

namespace std {

struct SseStream {
    struct TcpStream s;
    int              ok;   // 0 once a write failed (the client left)

    // Writes "200 OK" with Content-Type: text/event-stream, Cache-Control:
    // no-cache and 'extra_headers' (null or "Name: value" lines), and
    // detaches the connection.
    static struct SseStream start(&HttpConn conn, const ?&char extra_headers);
    // "data: ..." (one data: line per line of 'data') + blank line.
    int send(const &char data);
    // With an optional event name and id (null to omit).
    int send_event(const ?&char event, const &char data, const ?&char id);
    // ": text" — a comment, the usual keep-alive ping.
    int comment(const &char text);
    // "retry: ms" — the client's reconnection delay.
    int retry(int ms);
    void drop();
    void close() consume;   // = drop()
};

struct SseEvent {
    struct String event;   // "message" when the stream gave none
    struct String data;    // data: lines joined with '\n'
    struct String id;      // the last event id seen (sticky, as EventSource)
    int           retry;   // retry: value from this event, -1 if none
    void drop();

    void free() consume;   // = drop()
};

struct SseParser {
    struct String buf;         // unconsumed bytes
    struct String data;
    struct String event;
    struct String last_id;
    int           has_data;
    int           retry;

    static struct SseParser new();
    // Adds received bytes.
    void feed([]const unsigned char bytes);
    // internal: the (reference, length) core of feed()
    void feed_n_(const &unsigned char bytes, unsigned long len);
    // The next complete event (1, 'out' filled — free it) or 0 if more
    // input is needed. Comment lines and events without data are skipped.
    int  next(&SseEvent out);
    void drop();
    void free() consume;   // = drop()
};

struct SseClient {
    int              fd;
    ?&heap TlsStream tls;       // the TLS session for https://, else null
    int              chunked;   // the response used chunked transfer coding
    unsigned long    chunk_left;
    int              eof;
    struct SseParser parser;
    struct String    raw;       // undecoded bytes when chunked

    // GETs 'url' (http:// or https://) with Accept: text/event-stream and
    // checks for a 200 text/event-stream response. 'last_event_id' (or
    // null) is sent as Last-Event-ID. *ok = 0 on any failure. connect_tls
    // takes https:// settings ('cfg' null: TlsConfig::new()'s defaults).
    static struct SseClient connect(const &char url, const ?&char last_event_id, int timeout_ms, ?&int ok);
    static struct SseClient connect_tls(const &char url, const ?&char last_event_id, const ?&TlsConfig cfg,
                                        int timeout_ms, ?&int ok);
    // Blocks for the next event: 1 with 'out' filled, 0 once the stream
    // ended or failed.
    int  next(&SseEvent out);
    void drop();
    void close() consume;   // = drop()
};

} // namespace std