iodine 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17878,39 +17878,17 @@ int main(void) {
17878
17878
  #include FIO_INCLUDE_FILE
17879
17879
  ```
17880
17880
 
17881
- IPC is the messaging backbone of the facil.io worker/master model. Workers
17882
- fork from a master process. The master keeps listeners, manages state, and
17883
- handles anything that needs a single authoritative view of the world. Workers
17884
- handle client connections. IPC bridges the two: a worker sends a message to
17885
- the master (or to every process in the cluster), the master executes it and
17886
- optionally streams replies back.
17881
+ IPC is the messaging backbone of the facil.io worker/master model. Workers fork from a master process. The master keeps listeners, manages state, and handles anything that needs a single authoritative view of the world. Workers handle client connections. IPC bridges the two: a worker sends a message to the master (or to every process in the cluster), the master executes it and optionally streams replies back.
17887
17882
 
17888
- All messages are encrypted in transit with ChaCha20-Poly1305 AEAD, keyed from
17889
- the process secret. Local IPC runs over a Unix socket (or auto-generated TCP
17890
- socket). Multi-machine RPC extends the same protocol over TCP with optional
17891
- UDP peer discovery.
17883
+ IPC supports local messages and, when configured with a shared secret, messages between cluster peers. Register op-codes for messages sent between machines; do not send function pointers across machines.
17892
17884
 
17893
- See [./400 io-overview.md](./400 io-overview.md) for where IPC sits in the
17894
- full IO stack.
17885
+ See [./400 io-overview.md](./400 io-overview.md) for where IPC sits in the full IO stack.
17895
17886
 
17896
17887
  ---
17897
17888
 
17898
17889
  ## Worker/Master Model
17899
17890
 
17900
- ```
17901
- master process
17902
- ├── owns IPC listener socket
17903
- ├── executes messages sent by workers
17904
- ├── forwards cluster/broadcast messages to other machines
17905
- └── worker process × N
17906
- ├── each has its own IO reactor loop
17907
- ├── connects back to master via IPC socket on startup
17908
- └── sends fio_ipc_* messages to reach master or peers
17909
- ```
17910
-
17911
- The master is always the hub. Workers can only talk *through* the master —
17912
- there is no direct worker-to-worker channel. On shutdown the master sends a
17913
- local broadcast instructing all workers to stop.
17891
+ Workers send local messages through the master. Use `fio_ipc_call` to reach the master, or `fio_ipc_local` to reach the master and local workers. For cluster peers, use `fio_ipc_cluster` or `fio_ipc_broadcast`.
17914
17892
 
17915
17893
  ---
17916
17894
 
@@ -17931,28 +17909,24 @@ Define before including the header to override defaults.
17931
17909
 
17932
17910
  ```c
17933
17911
  typedef struct fio_ipc_s {
17934
- fio_io_s *from; /* originating IO (set by receiver; not transmitted) */
17935
- /* -- wire format starts here -- */
17936
- uint32_t len; /* data[] byte count (AAD - authenticated) */
17937
- uint16_t flags; /* user-settable flags (AAD - authenticated) */
17938
- uint16_t routing_flags; /* internal routing (AAD - authenticated) */
17939
- uint64_t timestamp; /* millisecond tick — part of nonce (unencrypted) */
17940
- uint64_t id; /* random 64-bit value — part of nonce (unencrypted) */
17912
+ fio_io_s *from; /* originating IO on receipt */
17913
+ uint32_t len; /* payload length */
17914
+ uint16_t flags; /* application flags */
17915
+ uint16_t routing_flags; /* routing state; do not edit directly */
17916
+ uint64_t timestamp; /* message timestamp */
17917
+ uint64_t id; /* message ID */
17941
17918
  union {
17942
- void (*call)(struct fio_ipc_s *); /* function to call (local IPC; encrypted) */
17943
- uint32_t opcode; /* registered op-code (cluster RPC; encrypted)*/
17919
+ void (*call)(struct fio_ipc_s *); /* local handler */
17920
+ uint32_t opcode; /* registered handler ID */
17944
17921
  };
17945
- void (*on_reply)(struct fio_ipc_s *); /* run on caller when reply arrives (encrypted) */
17946
- void (*on_done)(struct fio_ipc_s *); /* run on caller when last reply arrives (encrypted) */
17947
- void *udata; /* caller-side opaque pointer (encrypted) */
17948
- char data[]; /* payload + 16-byte Poly1305 MAC at tail (encrypted) */
17922
+ void (*on_reply)(struct fio_ipc_s *); /* reply handler */
17923
+ void (*on_done)(struct fio_ipc_s *); /* final reply handler */
17924
+ void *udata; /* caller context */
17925
+ char data[]; /* payload; read only len bytes */
17949
17926
  } fio_ipc_s;
17950
17927
  ```
17951
17928
 
17952
- Messages are reference-counted. Everything from `call` onward (including the
17953
- payload) is encrypted on the wire. `from`, `timestamp`, and `id` are unencrypted
17954
- and unauthenticated (they form the nonce). `len`, `flags`, and `routing_flags`
17955
- are unencrypted but authenticated via AEAD.
17929
+ Messages are reference-counted. In callbacks, `data` contains `len` payload bytes; do not assume it is null-terminated. Treat routing state as read-only.
17956
17930
 
17957
17931
  ### `fio_ipc_args_s` — construction arguments
17958
17932
 
@@ -18008,8 +17982,7 @@ typedef struct {
18008
17982
  (fio_buf_info_s[]){ __VA_ARGS__, { .len = ((size_t)-1) } }
18009
17983
  ```
18010
17984
 
18011
- Composes multiple buffers into a single message without intermediate
18012
- allocation. Iteration stops at the first entry with `len == (size_t)-1`.
17985
+ Composes multiple buffers into a single message without intermediate allocation. Iteration stops at the first entry with `len == (size_t)-1`.
18013
17986
 
18014
17987
  ```c
18015
17988
  uint32_t seq = 42;
@@ -18026,8 +17999,7 @@ fio_ipc_call(.call = my_handler,
18026
17999
 
18027
18000
  ## Delivery Macros
18028
18001
 
18029
- All four macros call `fio_ipc_new(...)` then `fio_ipc_send(...)`. They accept
18030
- named fields from `fio_ipc_args_s`.
18002
+ All four macros call `fio_ipc_new(...)` then `fio_ipc_send(...)`. They accept named fields from `fio_ipc_args_s`.
18031
18003
 
18032
18004
  | Macro | Delivered to |
18033
18005
  |-------|-------------|
@@ -18047,11 +18019,9 @@ named fields from `fio_ipc_args_s`.
18047
18019
  #define fio_ipc_broadcast(...) fio_ipc_send(fio_ipc_new(.workers = 1, .cluster = 1, __VA_ARGS__))
18048
18020
  ```
18049
18021
 
18050
- `fio_ipc_cluster` and `fio_ipc_broadcast` require an `opcode` (not a function
18051
- pointer) because function pointers are meaningless across machine boundaries.
18022
+ `fio_ipc_cluster` and `fio_ipc_broadcast` require an `opcode` (not a function pointer) because function pointers are meaningless across machine boundaries.
18052
18023
 
18053
- Use `.exclude = FIO_IPC_EXCLUDE_SELF` to skip the calling process when
18054
- broadcasting locally.
18024
+ Use `.exclude = FIO_IPC_EXCLUDE_SELF` to skip the calling process when broadcasting locally.
18055
18025
 
18056
18026
  **`FIO_IPC_EXCLUDE_SELF`**
18057
18027
 
@@ -18059,8 +18029,7 @@ broadcasting locally.
18059
18029
  #define FIO_IPC_EXCLUDE_SELF ((fio_io_s *)((char *)-1LL))
18060
18030
  ```
18061
18031
 
18062
- Pass as `.exclude` in `fio_ipc_args_s`, or set `ipc->from = FIO_IPC_EXCLUDE_SELF`
18063
- before calling `fio_ipc_send`.
18032
+ Pass as `.exclude` in `fio_ipc_args_s`, or set `ipc->from = FIO_IPC_EXCLUDE_SELF` before calling `fio_ipc_send`.
18064
18033
 
18065
18034
  ---
18066
18035
 
@@ -18130,9 +18099,7 @@ SFUNC void fio_ipc_reply(fio_ipc_reply_args_s args);
18130
18099
  fio_ipc_reply((fio_ipc_reply_args_s){ .ipc = (r), __VA_ARGS__ })
18131
18100
  ```
18132
18101
 
18133
- Send one or more replies from the master back to the caller. Set `.done = 1`
18134
- on the final reply to trigger the caller's `on_done` callback. Replies inherit
18135
- `call`, `on_reply`, `on_done`, and `udata` from the original request.
18102
+ Send one or more replies from the master back to the caller. Set `.done = 1` on the final reply to trigger the caller's `on_done` callback. Replies inherit `call`, `on_reply`, `on_done`, and `udata` from the original request.
18136
18103
 
18137
18104
  Streaming replies:
18138
18105
 
@@ -18152,8 +18119,7 @@ void stream_handler(fio_ipc_s *msg) {
18152
18119
 
18153
18120
  ## Op-Code Registration
18154
18121
 
18155
- Op-codes let you perform cluster-wide RPC without transmitting function
18156
- pointers (which differ per binary). Register before `fio_io_start`.
18122
+ Op-codes select handlers for cluster messages. Register them before `fio_io_start` on every participating process.
18157
18123
 
18158
18124
  ```c
18159
18125
  SFUNC int fio_ipc_opcode_register(fio_ipc_opcode_s opcode);
@@ -18166,7 +18132,7 @@ SFUNC const fio_ipc_opcode_s *fio_ipc_opcode(uint32_t opcode);
18166
18132
  ```
18167
18133
 
18168
18134
  - Op-codes must be non-zero `uint32_t` values.
18169
- - Values `>= 0xFF000000` are reserved for internal use.
18135
+ - Values `>= 0xFF000000` are reserved; choose a lower value.
18170
18136
  - Pass `call = NULL` to unregister.
18171
18137
  - Must be called before `fio_io_start()` (not thread-safe after that).
18172
18138
  - Returns 0 on success, -1 on error.
@@ -18185,8 +18151,7 @@ fio_ipc_opcode_register(.opcode = OP_SYNC, .call = NULL);
18185
18151
 
18186
18152
  ## IPC URL
18187
18153
 
18188
- The IPC socket URL is auto-generated at startup (`unix://fio_tmp_<rand>.sock`
18189
- in `$TMPDIR`). Override before `fio_io_start` on the master:
18154
+ The IPC URL is generated at startup. The master can override it before `fio_io_start`:
18190
18155
 
18191
18156
  ```c
18192
18157
  /** Returns current IPC socket URL. */
@@ -18225,14 +18190,59 @@ SFUNC void fio_ipc_cluster_connect(const char *url);
18225
18190
  SFUNC uint16_t fio_ipc_cluster_port(void);
18226
18191
  ```
18227
18192
 
18228
- - Call `fio_ipc_cluster_listen` from the master before or after
18229
- `fio_io_start`. It opens a TCP listener and a UDP broadcast socket on the
18230
- same port for automatic peer discovery.
18231
- - Uses the environment's shared secret for encryption (ChaCha20-Poly1305,
18232
- no forward secrecy). Set `SECRET=<shared-key>` across all instances.
18193
+ ### Cluster Peer Connection Callbacks
18194
+
18195
+ #### `FIO_IPC_CLUSTER_PEER_MAX_SUBSCRIBERS`
18196
+
18197
+ ```c
18198
+ #define FIO_IPC_CLUSTER_PEER_MAX_SUBSCRIBERS 32
18199
+ ```
18200
+
18201
+ At most 32 peer lifecycle callbacks can be registered per process. Further registrations return `-1`.
18202
+
18203
+ #### `fio_ipc_cluster_on_peer_s` and `fio_ipc_cluster_on_peer`
18204
+
18205
+ ```c
18206
+ typedef struct fio_ipc_cluster_on_peer_s {
18207
+ void (*on_attached)(fio_ipc_s *event); /* optional */
18208
+ void (*on_detached)(fio_ipc_s *event); /* optional */
18209
+ void *udata; /* callback context; borrowed */
18210
+ } fio_ipc_cluster_on_peer_s;
18211
+
18212
+ SFUNC int fio_ipc_cluster_on_peer(fio_ipc_cluster_on_peer_s args);
18213
+ #define fio_ipc_cluster_on_peer(...) \
18214
+ fio_ipc_cluster_on_peer((fio_ipc_cluster_on_peer_s){__VA_ARGS__})
18215
+ ```
18216
+
18217
+ Call on the master before `fio_io_start`. Each call adds a subscriber; at least one callback is required. `udata` may be NULL and remains caller-owned. Keep it valid while callbacks can run. Returns `0` on success, or `-1` for invalid callbacks, a worker, running IO, or full capacity. Registration is not thread-safe. There is no unsubscribe API.
18218
+
18219
+ Callbacks borrow a `fio_ipc_s` event whose `data` contains the peer UUID (`event->len == 16`). During `on_attached`, `event->from` identifies the connected peer. During `on_detached`, it is `NULL`. To retain an event beyond the callback, use `fio_ipc_dup` and later `fio_ipc_free`.
18220
+
18221
+ **Note:** To send to an attached peer, create a new opcode message and pass it with `event->from` to `fio_ipc_send_to` during the callback. The send takes ownership of the new message. Do not send or reply to the event itself. Keeping an attached event beyond the callback may delay its detach notification.
18222
+
18223
+ ```c
18224
+ #define PEER_HELLO_OP 42
18225
+ static void peer_hello(fio_ipc_s *message) { (void)message; }
18226
+ static void peer_attached(fio_ipc_s *event) {
18227
+ fio_ipc_s *message = fio_ipc_new(
18228
+ .cluster = 1, .opcode = PEER_HELLO_OP,
18229
+ .data = FIO_IPC_DATA(FIO_BUF_INFO1((char *)"hello")));
18230
+ if (message)
18231
+ fio_ipc_send_to(event->from, message);
18232
+ }
18233
+ static void peer_detached(fio_ipc_s *event) { (void)event; }
18234
+ /* On each peer before fio_io_start (illustrative setup statements): */
18235
+ if (fio_ipc_opcode_register(.opcode = PEER_HELLO_OP, .call = peer_hello) ||
18236
+ fio_ipc_cluster_on_peer(.on_attached = peer_attached,
18237
+ .on_detached = peer_detached)) {
18238
+ /* handle registration failure before starting IO */
18239
+ }
18240
+ ```
18241
+
18242
+ - Call `fio_ipc_cluster_listen` from the master before or after `fio_io_start` to accept and discover peers.
18243
+ - Set the same `SECRET` environment variable on all instances. Cluster encryption does not provide forward secrecy; rotate the secret by restarting instances.
18233
18244
  - All instances receive all `cluster` / `broadcast` messages.
18234
- - `fio_ipc_cluster_connect` lets you manually reach peers on other subnets
18235
- where UDP broadcast doesn't reach.
18245
+ - `fio_ipc_cluster_connect` lets you manually reach peers on other subnets where UDP broadcast doesn't reach.
18236
18246
 
18237
18247
  ```c
18238
18248
  /* Typical setup */
@@ -18245,19 +18255,17 @@ fio_io_start(4);
18245
18255
 
18246
18256
  ## Core Lifetime API
18247
18257
 
18248
- These are used internally by the delivery macros and are available for
18249
- advanced use (e.g., constructing messages for local execution, or async
18250
- message handling).
18258
+ Use these functions to construct, send, and retain messages when the delivery macros do not fit your use case.
18251
18259
 
18252
18260
  ```c
18253
18261
  /** Create a message without sending it. */
18254
18262
  SFUNC fio_ipc_s *fio_ipc_new(fio_ipc_args_s args);
18255
18263
  #define fio_ipc_new(...) fio_ipc_new((fio_ipc_args_s){ __VA_ARGS__ })
18256
18264
 
18257
- /** Encrypt, route, and free the message. Takes ownership. */
18265
+ /** Send the message according to its delivery options. Takes ownership. */
18258
18266
  SFUNC void fio_ipc_send(fio_ipc_s *ipc);
18259
18267
 
18260
- /** Encrypt and send directly to a specific IO. Takes ownership. */
18268
+ /** Send directly to a specific IO. Takes ownership. */
18261
18269
  SFUNC void fio_ipc_send_to(fio_io_s *to, fio_ipc_s *ipc);
18262
18270
 
18263
18271
  /** Increment reference count. */
@@ -18293,29 +18301,20 @@ void async_done(fio_ipc_s *msg) {
18293
18301
 
18294
18302
  ---
18295
18303
 
18296
- ## Encryption
18304
+ ## Message encryption (advanced)
18297
18305
 
18298
18306
  ```c
18299
- /** Encrypt a message in-place (idempotent). Called automatically by fio_ipc_send. */
18300
18307
  SFUNC void fio_ipc_encrypt(fio_ipc_s *m);
18301
-
18302
- /** Decrypt a message in-place. Returns 0 on success, non-zero on MAC failure. */
18303
18308
  FIO_IFUNC int fio_ipc_decrypt(fio_ipc_s *m);
18304
18309
  ```
18305
18310
 
18306
- ChaCha20-Poly1305 AEAD. Key derived from the shared process secret plus the
18307
- message's `id` and `timestamp` fields (which form the nonce). `len`, `flags`,
18308
- and `routing_flags` are authenticated but unencrypted. Everything else
18309
- (`call`/`opcode`, `on_reply`, `on_done`, `udata`, `data`) is encrypted.
18310
-
18311
- Decryption failure logs a security warning and the connection is closed.
18312
- Only processes sharing the same secret can communicate.
18311
+ Sending encrypts messages automatically. `fio_ipc_encrypt` encrypts a message in place; `fio_ipc_decrypt` returns `0` on success or nonzero if authentication fails. Most applications should use the delivery functions instead.
18313
18312
 
18314
18313
  ---
18315
18314
 
18316
18315
  ## Routing Flags
18317
18316
 
18318
- `routing_flags` is internal — do not edit manually. Available for inspection:
18317
+ The routing flags are defined in the header for advanced use. Prefer the delivery macros and `.flags` for application data. Do not modify `routing_flags` on a received message:
18319
18318
 
18320
18319
  ```c
18321
18320
  #define FIO_IPC_FLAG_ENCRYPTED ((uint16_t)1 << 0) /* message is encrypted */
@@ -18332,21 +18331,6 @@ Only processes sharing the same secret can communicate.
18332
18331
 
18333
18332
  ---
18334
18333
 
18335
- ## Keepalive
18336
-
18337
- IPC and cluster connections use an automatic ping/pong protocol. On the first
18338
- timeout the system sends a ping; if no pong arrives before the next timeout
18339
- the connection is closed. Ping frames are never delivered to user callbacks.
18340
-
18341
- Default timeouts (set in the protocol initializer):
18342
-
18343
- | Connection type | Timeout |
18344
- |-----------------|---------|
18345
- | Local IPC (master ↔ worker) | ~6 minutes |
18346
- | Cluster RPC (machine ↔ machine) | ~55 seconds |
18347
-
18348
- ---
18349
-
18350
18334
  ## Memory Management
18351
18335
 
18352
18336
  - Messages are reference-counted (`fio_ipc_dup` / `fio_ipc_free`).
@@ -18366,6 +18350,8 @@ Default timeouts (set in the protocol initializer):
18366
18350
  - `fio_ipc_dup` / `fio_ipc_free` are thread-safe.
18367
18351
  - `fio_ipc_opcode_register` is **not** thread-safe after `fio_io_start`.
18368
18352
  - `fio_ipc_url_set` must be called on the master before `fio_io_start`.
18353
+
18354
+ ---
18369
18355
  # OpenSSL TLS Backend
18370
18356
 
18371
18357
  ```c
@@ -20292,32 +20278,11 @@ support in [DEFLATE / Gzip](./162 deflate.md).
20292
20278
  - upgrade helpers for WebSocket and SSE connections.
20293
20279
  - pub/sub helpers for upgraded connections.
20294
20280
  - handle access helpers: `fio_http_io` and `fio_http_settings`.
20295
- - the HTTP handle API (handle and core implementation in
20296
- `432 http types.h`), including request / response fields, headers,
20281
+ - the HTTP handle API, including request / response fields, headers,
20297
20282
  cookies, body storage, writing, static-file responses, logging, MIME
20298
20283
  lookup, and controller hooks.
20299
20284
 
20300
- Implementation helpers named `fio___...` are private implementation details.
20301
-
20302
- ---
20303
-
20304
- ## Module Files
20305
-
20306
- All HTTP module files share the `FIO_HTTP` flag.
20307
-
20308
- - `430 http api.h` — public declarations: settings, listener / route /
20309
- client APIs, and the full handle API.
20310
- - `432 http types.h` — internal types plus the handle and core
20311
- implementation (header cache, body storage, static-file responses).
20312
- - `434 http accept.h` — accept path, request dispatchers, upgrade
20313
- authorization.
20314
- - `434 http1.h` — HTTP/1.1 request / response glue, protocol and
20315
- controller.
20316
- - `434 sse.h` — EventSource (SSE) upgrade, helpers, protocol, controller.
20317
- - `434 websocket.h` — WebSocket upgrade, events, protocol, write,
20318
- controller.
20319
- - `438 http.h` — listen / connect glue, protocol wiring, shared helpers.
20320
- - `439 http.h` — cleanup tail (the module's only `#undef FIO_HTTP` site).
20285
+ Names prefixed with `fio___` are private and not part of the API.
20321
20286
 
20322
20287
  The parsers are separate modules: [`004 http1 parser.h`](./004 http1 parser.md)
20323
20288
  and [`004 websocket parser.h`](./004 websocket parser.md).
@@ -20417,7 +20382,7 @@ Other settings:
20417
20382
  | `sse_timeout` | SSE timeout; timeout pings are sent. |
20418
20383
  | `connect_timeout` | Client connection timeout. |
20419
20384
  | `log` | Enables HTTP request logging. |
20420
- | `compress_static` | Opt-in static-file compression (on-demand `.br`/`.gz` creation). Per-route; routes inherit the listener's root value at route-creation. The value is a failure-memoization shift register (see *Static File Service*). Detached handles gate on the `FIO_HTTP_CFLAG_COMPRESS_STATIC` cflag instead. See the compression security note below. |
20385
+ | `compress_static` | Opt-in static-file compression (on-demand `.br`/`.gz` creation). Per-route; routes inherit the listener's root value at route-creation. May drop to 0 when creation is disabled after failures (see *Static File Service*). See the compression security note below. |
20421
20386
  | `compress_dynamic` | Opt-in dynamic response compression. Connection-global; per-route values are ignored. See the compression security note below. |
20422
20387
  | `compress_ws` | Opt-in WebSocket `permessage-deflate`. Connection-global; per-route values are ignored. See the compression security note below. |
20423
20388
 
@@ -20425,21 +20390,18 @@ Other settings:
20425
20390
  secrets together with attacker-reflected data enables length-oracle attacks
20426
20391
  (BREACH/CRIME). Do not enable compression for responses that mix secrets
20427
20392
  (CSRF tokens, session data) with attacker-controlled reflections, or emit
20428
- such messages uncompressed per call (per-message opt-out: keep the cflag off
20429
- at the route level for sensitive routes, or pre-set `content-encoding`
20430
- — which the dynamic path always respects — to pass a body through
20431
- uncompressed).
20393
+ such messages uncompressed: leave `compress_dynamic` / `compress_ws` off
20394
+ for listeners serving them, or pre-set a response `content-encoding` header
20395
+ (it is always respected) to pass a body through uncompressed.
20432
20396
 
20433
20397
  **WebSocket `permessage-deflate` negotiation:** the server ALWAYS responds
20434
20398
  with `permessage-deflate; server_no_context_takeover;
20435
20399
  client_no_context_takeover` (RFC 7692 allows either endpoint to request the
20436
20400
  flags unilaterally), honoring `server_max_window_bits` when offered (the
20437
20401
  compressor clamps its distances accordingly) and never emitting window-bits
20438
- parameters. Cross-message history is never used, so persistent compression
20439
- state per connection is ~0 (two ~32-byte contexts plus a bounded input
20440
- buffer, shrunk on every message reset); compressor scratch comes from a
20441
- static process-wide slot pool checked out per call — under extreme
20442
- contention a message simply goes out uncompressed.
20402
+ parameters. Cross-message history is never used, keeping per-connection
20403
+ compression memory minimal. Under heavy load, a message may be sent
20404
+ uncompressed.
20443
20405
 
20444
20406
  **Accept-Encoding handling:** `q=0` (in any zero-padded form) forbids an
20445
20407
  encoding (RFC 9110). `Vary: accept-encoding` is set whenever compressed
@@ -20613,33 +20575,29 @@ least as fresh as the original (modification time) is served, with
20613
20575
  selected (RFC 9110 §12.5.3).
20614
20576
 
20615
20577
  With `compress_static` enabled, missing or stale `.br` / `.gz` variants are
20616
- created on demand and written into the root folder. Attached handles read the
20617
- flag from the matching route's settings (routes inherit the listener's root
20618
- value at route-creation); detached handles (created manually, with no
20619
- settings) gate on the `FIO_HTTP_CFLAG_COMPRESS_STATIC` handle cflag instead.
20578
+ created on demand and written into the root folder. The value is read from the
20579
+ matching route's settings (routes inherit the listener's root value at
20580
+ route-creation); handles without settings (created with `fio_http_new`) never
20581
+ create variants.
20620
20582
 
20621
20583
  - creation is limited to compressible (text-like) MIME types and originals
20622
20584
  between 1024 bytes and `FIO_HTTP_STATIC_FILE_COMPRESS_LIMIT` (2 MiB);
20623
20585
  - creation is skipped when compression would not shrink the file;
20624
- - `.br` variants are compressed at brotli quality 4 (the benchmark-reviewed
20625
- fast-path ceiling; q5+ engages the slow hash-chain path) and `.gz`
20626
- variants at gzip level 6;
20586
+ - `.br` variants are compressed at brotli quality 4 and `.gz` variants at
20587
+ gzip level 6;
20627
20588
  - `zstd` / `deflate` variants are only ever served pre-generated — they are
20628
20589
  never created on demand;
20629
20590
  - on-demand creation writes into the served folder and is triggered by
20630
20591
  client requests; the folder must be writable for creation to succeed
20631
- (a write failure disables creation, see memoization below). Variants
20592
+ (repeated failures disable creation, see below). Variants
20632
20593
  may also be pre-generated at deploy time with any external tool — the
20633
20594
  server only stats the files.
20634
20595
 
20635
- `compress_static` is a failure-memoization shift register, updated atomically
20636
- (the IO threads share the listener / route settings): any non-zero value
20637
- enables on-demand creation. A compression success re-seeds bit 0
20638
- (`value |= 1`); any other failure shifts the value left (`value <<= 1`), so
20639
- 8 consecutive failures shift out of the `uint8_t` and disable creation, as
20640
- does a single filesystem-full / permission error (`ENOSPC`, `EACCES`,
20641
- `EROFS`, `EDQUOT` — `value = 0`). Once 0, creation stays disabled until the
20642
- settings are re-applied. Detached handles carry no memoization state.
20596
+ Any non-zero `compress_static` value enables on-demand creation. The server
20597
+ disables creation (setting the value to 0) after repeated consecutive
20598
+ failures, or after a single filesystem-full / permission error (`ENOSPC`,
20599
+ `EACCES`, `EROFS`, `EDQUOT`). Once disabled, creation stays off until the
20600
+ settings are re-applied.
20643
20601
 
20644
20602
  `Vary: accept-encoding` is set whenever a variant is served, and on
20645
20603
  identity responses whenever variants may be created, so caches key on the
@@ -20856,12 +20814,35 @@ fio_http_controller_s *fio_http_controller_set(fio_http_s *h,
20856
20814
  `udata` and `udata2` are for application state. `cdata` and the controller are
20857
20815
  used by the HTTP layer and custom controllers.
20858
20816
 
20817
+ `fio_http_controller` gets the handle's controller; `fio_http_controller_set`
20818
+ sets it and returns the controller in use. Passing `NULL` installs a no-op
20819
+ controller. Missing callbacks in a custom controller are filled in with no-op
20820
+ defaults, so the controller object must be writable and outlive the handle.
20821
+
20859
20822
  ### Request / response properties
20860
20823
 
20861
20824
  ```c
20862
20825
  size_t fio_http_status(fio_http_s *h);
20863
20826
  size_t fio_http_status_set(fio_http_s *h, size_t status);
20864
20827
  fio_str_info_s fio_http_method(fio_http_s *h);
20828
+ int fio_http_method_is_get(fio_http_s *h);
20829
+ int fio_http_method_is_head(fio_http_s *h);
20830
+ int fio_http_method_is_post(fio_http_s *h);
20831
+ int fio_http_method_is_put(fio_http_s *h);
20832
+ int fio_http_method_is_delete(fio_http_s *h);
20833
+ int fio_http_method_is_connect(fio_http_s *h);
20834
+ int fio_http_method_is_options(fio_http_s *h);
20835
+ int fio_http_method_is_trace(fio_http_s *h);
20836
+ int fio_http_method_is_patch(fio_http_s *h);
20837
+ int fio_http_method_is_query(fio_http_s *h);
20838
+ int fio_http_method_is_propfind(fio_http_s *h);
20839
+ int fio_http_method_is_proppatch(fio_http_s *h);
20840
+ int fio_http_method_is_mkcol(fio_http_s *h);
20841
+ int fio_http_method_is_copy(fio_http_s *h);
20842
+ int fio_http_method_is_move(fio_http_s *h);
20843
+ int fio_http_method_is_lock(fio_http_s *h);
20844
+ int fio_http_method_is_unlock(fio_http_s *h);
20845
+ int fio_http_method_is_search(fio_http_s *h);
20865
20846
  fio_str_info_s fio_http_method_set(fio_http_s *h, fio_str_info_s value);
20866
20847
  fio_str_info_s fio_http_opath(fio_http_s *h);
20867
20848
  fio_str_info_s fio_http_opath_set(fio_http_s *h, fio_str_info_s value);
@@ -20878,10 +20859,18 @@ int64_t fio_http_received_at_set(fio_http_s *h, int64_t value);
20878
20859
  `opath` is the original path before route prefix trimming. `path` is the routed
20879
20860
  path visible to the current callback.
20880
20861
 
20881
- `fio_http_status_set(h, 0)` normalizes to `200`; values above the internal range
20882
- normalize to `500`.
20862
+ The `fio_http_method_is_*` helpers test whether the request method matches the
20863
+ named method, ignoring ASCII case. They return `1` for a match and `0` otherwise;
20864
+ pass a non-`NULL` HTTP handle (`h`). Unlike `fio_http_resource_action`, these
20865
+ helpers do not classify paths or combine PUT, POST, and PATCH into a single
20866
+ action. The helpers cover the core HTTP methods, PATCH, QUERY, and commonly
20867
+ used WebDAV methods (PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, and
20868
+ SEARCH).
20883
20869
 
20884
- ### State checks and flags
20870
+ `fio_http_status_set(h, 0)` normalizes to `200`; values above `1023` normalize
20871
+ to `500`.
20872
+
20873
+ ### State checks
20885
20874
 
20886
20875
  ```c
20887
20876
  int fio_http_is_clean(fio_http_s *h);
@@ -20893,25 +20882,6 @@ int fio_http_is_sse(fio_http_s *h);
20893
20882
  int fio_http_is_freeing(fio_http_s *h);
20894
20883
  ```
20895
20884
 
20896
- The handle also exposes inline flag helpers generated for `cflags` and
20897
- `uflags`:
20898
-
20899
- ```c
20900
- uint16_t fio_http_cflags(fio_http_s *h);
20901
- uint16_t fio_http_cflags_set(fio_http_s *h, uint16_t flags);
20902
- uint16_t fio_http_cflags_unset(fio_http_s *h, uint16_t flags);
20903
- uint16_t fio_http_cflags_flip(fio_http_s *h, uint16_t flags);
20904
- uint16_t fio_http_cflags_is_set(fio_http_s *h, uint16_t flags);
20905
-
20906
- uint16_t fio_http_uflags(fio_http_s *h);
20907
- uint16_t fio_http_uflags_set(fio_http_s *h, uint16_t flags);
20908
- uint16_t fio_http_uflags_unset(fio_http_s *h, uint16_t flags);
20909
- uint16_t fio_http_uflags_flip(fio_http_s *h, uint16_t flags);
20910
- uint16_t fio_http_uflags_is_set(fio_http_s *h, uint16_t flags);
20911
- ```
20912
-
20913
- Prefer the state helpers unless implementing protocol or controller glue.
20914
-
20915
20885
  ### Headers
20916
20886
 
20917
20887
  ```c
@@ -21132,13 +21102,10 @@ void fio_http_write_log(fio_http_s *h);
21132
21102
  int fio_http_from(fio_str_info_s *dest, const fio_http_s *h);
21133
21103
  fio_str_info_s fio_http_date(uint64_t now_in_seconds);
21134
21104
  fio_str_info_s fio_http_log_time(uint64_t now_in_seconds);
21135
- int64_t fio_http_get_timestump(void);
21136
21105
  ```
21137
21106
 
21138
- `fio_http_get_timestump` is the inline timestamp helper used by the handle for
21139
- `received_at` and log timing. User code usually reads timestamps with
21140
- `fio_http_received_at` or formats them with `fio_http_date` /
21141
- `fio_http_log_time`.
21107
+ Read request timestamps with `fio_http_received_at`; format dates with
21108
+ `fio_http_date` / `fio_http_log_time`.
21142
21109
 
21143
21110
  `fio_http_from` writes a best-effort peer address starting with the `Forwarded`
21144
21111
  header, then socket peer address, then `"[unknown]"`.
@@ -21179,7 +21146,8 @@ struct fio_http_controller_s {
21179
21146
  ```
21180
21147
 
21181
21148
  Controllers allow the handle to write through different protocols. Initialize
21182
- custom controllers to zero before use. If controller callbacks are not
21149
+ custom controllers to zero before use; `private_flags` is reserved and must
21150
+ remain zero. If controller callbacks are not
21183
21151
  thread-safe, call `fio_http_write` only from the controller's expected thread.
21184
21152
 
21185
21153
  ---
@@ -21217,24 +21185,11 @@ FIO_HTTP_LOG_X_REQUEST_START /* 1 */
21217
21185
  FIO_HTTP_ENFORCE_LOWERCASE_HEADERS /* 0 */
21218
21186
  ```
21219
21187
 
21220
- State and controller flag macros are exposed for protocol glue:
21221
-
21222
- ```c
21223
- FIO_HTTP_STATE_STREAMING
21224
- FIO_HTTP_STATE_FINISHED
21225
- FIO_HTTP_STATE_UPGRADED
21226
- FIO_HTTP_STATE_WEBSOCKET
21227
- FIO_HTTP_STATE_SSE
21228
- FIO_HTTP_STATE_COOKIES_PARSED
21229
- FIO_HTTP_STATE_FREEING
21230
-
21231
- FIO_HTTP_CFLAG_COMPRESS_DYNAMIC
21232
- FIO_HTTP_CFLAG_COMPRESS_WS
21233
- FIO_HTTP_CFLAG_COMPRESS_STATIC
21234
- ```
21235
-
21236
- Prefer public helper functions over reading state bits directly in application
21237
- code.
21188
+ With `FIO_HTTP_LOG_X_REQUEST_START`, request logs (`.log = 1`) include the
21189
+ request's wait time when the client or proxy sends an `X-Request-Start`
21190
+ header holding a millisecond timestamp (optionally prefixed with `t=`). The
21191
+ wait time is omitted when the header is missing or the wait is 512 seconds or
21192
+ more.
21238
21193
 
21239
21194
  ---
21240
21195
 
@@ -247,7 +247,7 @@ FIO_SFUNC VALUE iodine_utils_is_eq(VALUE mod, VALUE a, VALUE b) { // clang-forma
247
247
  Randomness and Friends
248
248
  ***************************************************************************** */
249
249
 
250
- FIO_DEFINE_RANDOM128_FN(static, iodine_random, 10, 0)
250
+ FIO_DEFINE_RANDOM128_FN(static, iodine_random, 11, 0)
251
251
 
252
252
  FIO_SFUNC VALUE iodine_utils_hmac512(VALUE self, VALUE secret, VALUE massage) {
253
253
  rb_check_type(secret, RUBY_T_STRING);
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Iodine
4
4
  # The Iodine gem version number
5
- VERSION = "0.8.1"
5
+ VERSION = "0.8.2"
6
6
  # NeoRack supported extensions.
7
7
  def self.extensions
8
8
  return (@extensions ||= { iodine: (VERSION.split(?.).map() {|i| i.to_i.to_s == i ? i.to_i : i }),
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
@@ -133,7 +133,7 @@ metadata:
133
133
  homepage_uri: https://github.com/boazsegev/iodine
134
134
  source_code_uri: https://github.com/boazsegev/iodine
135
135
  changelog_uri: https://github.com/boazsegev/iodine/blob/master/CHANGELOG.md
136
- post_install_message: Thank you for installing Iodine 0.8.1.
136
+ post_install_message: Thank you for installing Iodine 0.8.2.
137
137
  rdoc_options: []
138
138
  require_paths:
139
139
  - lib