@gjsify/http2-native 0.4.3 → 0.4.5

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.
@@ -1,13 +1,14 @@
1
1
  /*
2
- * Tiny C shim around libnghttp2's HPACK encoder + frame helpers.
2
+ * Tiny C shim around libnghttp2's HPACK encoder + frame helpers + session API.
3
3
  *
4
4
  * Vala can bind nghttp2 directly, but the calling conventions for opaque
5
- * out-pointer constructors (nghttp2_hd_deflate_new) and pointer-array
6
- * structs (nghttp2_nv) are awkward to express across a VAPI we have to
7
- * maintain by hand. Wrapping them in plain C functions returning GBytes
8
- * is far simpler — and keeps every pointer hand-off inside C, which is
9
- * exactly the pattern @gjsify/http-soup-bridge / @gjsify/webrtc-native
10
- * use to keep SpiderMonkey GC out of the libnghttp2 boxed lifecycle.
5
+ * out-pointer constructors (nghttp2_hd_deflate_new), pointer-array structs
6
+ * (nghttp2_nv) and the session-callback machinery are awkward to express
7
+ * across a VAPI we have to maintain by hand. Wrapping them in plain C
8
+ * functions returning GBytes / opaque pointers is far simpler — and keeps
9
+ * every pointer hand-off inside C, which is exactly the pattern
10
+ * @gjsify/http-soup-bridge / @gjsify/webrtc-native use to keep
11
+ * SpiderMonkey GC out of the libnghttp2 boxed lifecycle.
11
12
  *
12
13
  * Reference: nghttp2/nghttp2.h (libnghttp2 ≥ 1.40).
13
14
  */
@@ -16,10 +17,15 @@
16
17
  #define GJSIFY_HTTP2_NGHTTP2_HELPERS_H
17
18
 
18
19
  #include <glib.h>
20
+ #include <glib-object.h>
19
21
  #include <nghttp2/nghttp2.h>
20
22
 
21
23
  G_BEGIN_DECLS
22
24
 
25
+ /* ────────────────────────────────────────────────────────────────────── *
26
+ * HPACK + standalone frame builders (used by FrameEncoder)
27
+ * ────────────────────────────────────────────────────────────────────── */
28
+
23
29
  /**
24
30
  * gjsify_http2_hpack_encode:
25
31
  * @names: flat name array (length = n_pairs)
@@ -77,6 +83,245 @@ GBytes *gjsify_http2_pack_push_promise (guint32 associated_stream_id,
77
83
  */
78
84
  const char *gjsify_http2_nghttp2_version (void);
79
85
 
86
+ /* ────────────────────────────────────────────────────────────────────── *
87
+ * Session API — opaque wrapper around an nghttp2_session + event queue.
88
+ *
89
+ * Events are captured C-side via nghttp2 callbacks and stored in a GQueue.
90
+ * The Vala-side wrapper drains the queue (gjsify_http2_session_drain_events)
91
+ * and re-emits each entry as a GLib signal via GLib.Idle.add() so JS-visible
92
+ * dispatch happens on the main loop thread, matching the webrtc-native
93
+ * streaming-thread → main-thread pattern.
94
+ *
95
+ * Threading model: nghttp2 callbacks run inside `gjsify_http2_session_recv`
96
+ * (called on the main loop in this codebase since I/O is GLib-driven), so
97
+ * we never need a cross-thread queue; the Idle.add() hop is purely to
98
+ * decouple JS dispatch from active I/O frames (avoid re-entrancy).
99
+ * ────────────────────────────────────────────────────────────────────── */
100
+
101
+ /* Opaque session handle. */
102
+ typedef struct _GjsifyHttp2Session GjsifyHttp2Session;
103
+
104
+ /* Mode flags for session_new. */
105
+ typedef enum {
106
+ GJSIFY_HTTP2_SESSION_MODE_SERVER = 0,
107
+ GJSIFY_HTTP2_SESSION_MODE_CLIENT = 1
108
+ } GjsifyHttp2SessionMode;
109
+
110
+ /* Event kinds — kept stable across versions; appended only. */
111
+ typedef enum {
112
+ GJSIFY_HTTP2_EVENT_HEADERS = 1, /* request/response HEADERS complete */
113
+ GJSIFY_HTTP2_EVENT_DATA = 2, /* DATA chunk for a stream */
114
+ GJSIFY_HTTP2_EVENT_STREAM_CLOSED = 3, /* stream finished (after END_STREAM or RST_STREAM) */
115
+ GJSIFY_HTTP2_EVENT_GOAWAY = 4, /* GOAWAY frame received */
116
+ GJSIFY_HTTP2_EVENT_SETTINGS = 5, /* SETTINGS frame received */
117
+ GJSIFY_HTTP2_EVENT_PUSH_PROMISE = 6 /* PUSH_PROMISE received (client side) */
118
+ } GjsifyHttp2EventKind;
119
+
120
+ /**
121
+ * GjsifyHttp2Event:
122
+ *
123
+ * Opaque event record drained out of the session via
124
+ * gjsify_http2_session_drain_events(). Definition lives in
125
+ * nghttp2-helpers.c — accessed exclusively through the
126
+ * gjsify_http2_event_get_*() accessors below.
127
+ *
128
+ * Keeping the tag visible (forward declared here, full struct in .c)
129
+ * is what lets Vala emit a matching `typedef struct _GjsifyHttp2Event
130
+ * GjsifyHttp2Event;` forward declaration in its own generated header
131
+ * without redefining the tag.
132
+ */
133
+ typedef struct _GjsifyHttp2Event GjsifyHttp2Event;
134
+
135
+ void gjsify_http2_event_free (GjsifyHttp2Event *event);
136
+
137
+ /* ── Event getters — Vala bindings call these instead of accessing struct
138
+ * fields directly, so the Vala C generator never needs to emit a
139
+ * typedef for GjsifyHttp2Event (which would clash with this header).
140
+ *
141
+ * Header arrays are returned as plain `char**` whose lifetime stays
142
+ * pinned to the event itself; do NOT free them — they vanish when the
143
+ * enclosing event is freed via gjsify_http2_event_free(). The data
144
+ * GBytes return retains a ref from inside the event; if the caller
145
+ * needs to outlive the event, take an explicit ref.
146
+ * ───────────────────────────────────────────────────────────────────── */
147
+ guint32 gjsify_http2_event_get_kind (GjsifyHttp2Event *event);
148
+ guint32 gjsify_http2_event_get_stream_id (GjsifyHttp2Event *event);
149
+ guint32 gjsify_http2_event_get_error_code (GjsifyHttp2Event *event);
150
+ guint32 gjsify_http2_event_get_last_stream_id (GjsifyHttp2Event *event);
151
+ gboolean gjsify_http2_event_get_end_stream (GjsifyHttp2Event *event);
152
+ guint32 gjsify_http2_event_get_promised_stream_id (GjsifyHttp2Event *event);
153
+ gsize gjsify_http2_event_get_header_count (GjsifyHttp2Event *event);
154
+ const char * gjsify_http2_event_get_header_name (GjsifyHttp2Event *event, gsize index);
155
+ const char * gjsify_http2_event_get_header_value (GjsifyHttp2Event *event, gsize index);
156
+ GBytes * gjsify_http2_event_get_data (GjsifyHttp2Event *event);
157
+
158
+ /**
159
+ * gjsify_http2_session_new:
160
+ * @mode: server or client
161
+ *
162
+ * Creates a new session. Returns NULL on allocation failure. Free with
163
+ * gjsify_http2_session_free().
164
+ *
165
+ * The session is created with default settings; the caller is expected
166
+ * to call gjsify_http2_session_submit_settings_local() with whatever
167
+ * server/client SETTINGS frame is appropriate (we always send a
168
+ * default-shaped SETTINGS on the next drain).
169
+ */
170
+ GjsifyHttp2Session *gjsify_http2_session_new (GjsifyHttp2SessionMode mode);
171
+
172
+ void gjsify_http2_session_free (GjsifyHttp2Session *session);
173
+
174
+ /**
175
+ * gjsify_http2_session_feed:
176
+ * @session: the session
177
+ * @input: bytes received from the peer (may be %NULL only if len==0)
178
+ *
179
+ * Feed received bytes into nghttp2_session_mem_recv(). Returns the number
180
+ * of bytes consumed (always equal to the input length on success) or a
181
+ * negative nghttp2 error code on protocol/parse error.
182
+ *
183
+ * Side effect: events are appended to the internal event queue; drain
184
+ * via gjsify_http2_session_drain_events(). The function does NOT emit
185
+ * any output — call gjsify_http2_session_drain_output() afterwards.
186
+ */
187
+ gssize gjsify_http2_session_feed (GjsifyHttp2Session *session,
188
+ GBytes *input);
189
+
190
+ /**
191
+ * gjsify_http2_session_drain_output:
192
+ * @session: the session
193
+ *
194
+ * Returns a #GBytes containing all pending output frames (concatenated)
195
+ * that nghttp2 wants to send. Empty bytes are returned if nothing is
196
+ * pending. Caller MUST write these bytes to the socket; only after the
197
+ * write completes should further frames be drained.
198
+ */
199
+ GBytes *gjsify_http2_session_drain_output (GjsifyHttp2Session *session);
200
+
201
+ /**
202
+ * gjsify_http2_session_drain_events:
203
+ * @session: the session
204
+ *
205
+ * Drains every queued event into a NULL-terminated array of
206
+ * #GjsifyHttp2Event pointers. Caller owns the array AND each element
207
+ * (use gjsify_http2_event_free + g_free for the array container).
208
+ *
209
+ * Returns %NULL when the queue is empty.
210
+ */
211
+ GjsifyHttp2Event **gjsify_http2_session_drain_events (GjsifyHttp2Session *session,
212
+ gsize *out_n);
213
+
214
+ /**
215
+ * gjsify_http2_session_submit_settings:
216
+ * @session: the session
217
+ *
218
+ * Queues a SETTINGS frame with the default values. Idempotent: calling
219
+ * twice queues two SETTINGS frames (rare; for the server-side initial
220
+ * handshake we call it once right after session creation).
221
+ *
222
+ * Returns 0 on success, < 0 nghttp2 error code.
223
+ */
224
+ int gjsify_http2_session_submit_settings (GjsifyHttp2Session *session);
225
+
226
+ /**
227
+ * gjsify_http2_session_submit_response:
228
+ * @session: server session
229
+ * @stream_id: client-initiated stream we're responding to
230
+ * @names / @values: response header pairs (must include :status)
231
+ * @n_pairs: count of header pairs
232
+ * @end_stream: %TRUE if the response carries no body
233
+ *
234
+ * Submits HEADERS for the response. Returns 0 / < 0.
235
+ */
236
+ int gjsify_http2_session_submit_response (GjsifyHttp2Session *session,
237
+ guint32 stream_id,
238
+ char **names,
239
+ char **values,
240
+ gsize n_pairs,
241
+ gboolean end_stream);
242
+
243
+ /**
244
+ * gjsify_http2_session_submit_request:
245
+ * @session: client session
246
+ * @names / @values: request header pairs (must include :method/:scheme/:path)
247
+ * @n_pairs: count of header pairs
248
+ * @end_stream: %TRUE if the request carries no body
249
+ *
250
+ * Returns the freshly-allocated odd client stream id (1, 3, 5, ...), or
251
+ * 0 on error. Symmetric to submit_response on the server side.
252
+ */
253
+ guint32 gjsify_http2_session_submit_request (GjsifyHttp2Session *session,
254
+ char **names,
255
+ char **values,
256
+ gsize n_pairs,
257
+ gboolean end_stream);
258
+
259
+ /**
260
+ * gjsify_http2_session_submit_data:
261
+ * @session: the session
262
+ * @stream_id: target stream
263
+ * @data: payload bytes (may be %NULL only if end_stream is %TRUE)
264
+ * @end_stream: %TRUE marks last DATA on the stream
265
+ *
266
+ * Queues a DATA frame. Returns 0 on success, < 0 nghttp2 error code.
267
+ *
268
+ * Buffer ownership: nghttp2 reads via a data_source callback that the
269
+ * implementation backs with @data; we hold a ref on @data until the
270
+ * frame is fully transmitted (signalled via on_frame_send).
271
+ */
272
+ int gjsify_http2_session_submit_data (GjsifyHttp2Session *session,
273
+ guint32 stream_id,
274
+ GBytes *data,
275
+ gboolean end_stream);
276
+
277
+ /**
278
+ * gjsify_http2_session_submit_push_promise:
279
+ * @session: server session
280
+ * @parent_id: client stream this push is associated with
281
+ * @names / @values / @n_pairs: request headers for the push
282
+ *
283
+ * Returns the promised (server-allocated) stream id, or 0 on error.
284
+ * The caller can then submit_response()/submit_data() on the returned id.
285
+ */
286
+ guint32 gjsify_http2_session_submit_push_promise (GjsifyHttp2Session *session,
287
+ guint32 parent_id,
288
+ char **names,
289
+ char **values,
290
+ gsize n_pairs);
291
+
292
+ /**
293
+ * gjsify_http2_session_submit_goaway:
294
+ * @session: the session
295
+ * @last_stream_id: last processed stream id (set this BEFORE close)
296
+ * @error_code: nghttp2 error code (NO_ERROR=0, PROTOCOL_ERROR=1, ...)
297
+ *
298
+ * Queues a GOAWAY. Returns 0 / < 0 nghttp2 error code.
299
+ */
300
+ int gjsify_http2_session_submit_goaway (GjsifyHttp2Session *session,
301
+ guint32 last_stream_id,
302
+ guint32 error_code);
303
+
304
+ /**
305
+ * gjsify_http2_session_submit_rst_stream:
306
+ * @session: the session
307
+ * @stream_id: stream to reset
308
+ * @error_code: nghttp2 error code
309
+ *
310
+ * Queues a RST_STREAM. Returns 0 / < 0.
311
+ */
312
+ int gjsify_http2_session_submit_rst_stream (GjsifyHttp2Session *session,
313
+ guint32 stream_id,
314
+ guint32 error_code);
315
+
316
+ /**
317
+ * gjsify_http2_session_want_read / _want_write:
318
+ *
319
+ * Returns %TRUE if nghttp2 has more bytes to consume / emit. The driver
320
+ * uses these to decide whether to keep watching the socket for IN / OUT.
321
+ */
322
+ gboolean gjsify_http2_session_want_read (GjsifyHttp2Session *session);
323
+ gboolean gjsify_http2_session_want_write (GjsifyHttp2Session *session);
324
+
80
325
  G_END_DECLS
81
326
 
82
327
  #endif /* GJSIFY_HTTP2_NGHTTP2_HELPERS_H */