• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Programmers' Guide
2==================
3
4Architecture
5------------
6
7The most notable point in nghttp2 library architecture is it does not
8perform any I/O.  nghttp2 only performs HTTP/2 protocol stuff based on
9input byte strings.  It will call callback functions set by
10applications while processing input.  The output of nghttp2 is just
11byte string.  An application is responsible to send these output to
12the remote peer.  The callback functions may be called while producing
13output.
14
15Not doing I/O makes embedding nghttp2 library in the existing code
16base very easy.  Usually, the existing applications have its own I/O
17event loops.  It is very hard to use nghttp2 in that situation if
18nghttp2 does its own I/O.  It also makes light weight language wrapper
19for nghttp2 easy with the same reason.  The down side is that an
20application author has to write more code to write complete
21application using nghttp2.  This is especially true for simple "toy"
22application.  For the real applications, however, this is not the
23case.  This is because you probably want to support HTTP/1 which
24nghttp2 does not provide, and to do that, you will need to write your
25own HTTP/1 stack or use existing third-party library, and bind them
26together with nghttp2 and I/O event loop.  In this point, not
27performing I/O in nghttp2 has more point than doing it.
28
29The primary object that an application uses is :type:`nghttp2_session`
30object, which is opaque struct and its details are hidden in order to
31ensure the upgrading its internal architecture without breaking the
32backward compatibility.  An application can set callbacks to
33:type:`nghttp2_session` object through the dedicated object and
34functions, and it also interacts with it via many API function calls.
35
36An application can create as many :type:`nghttp2_session` object as it
37wants.  But single :type:`nghttp2_session` object must be used by a
38single thread at the same time.  This is not so hard to enforce since
39most event-based architecture applications use is single thread per
40core, and handling one connection I/O is done by single thread.
41
42To feed input to :type:`nghttp2_session` object, one can use
43`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` functions.
44They behave similarly, and the difference is that
45`nghttp2_session_recv()` will use :type:`nghttp2_read_callback` to get
46input.  On the other hand, `nghttp2_session_mem_recv()` will take
47input as its parameter.  If in doubt, use `nghttp2_session_mem_recv()`
48since it is simpler, and could be faster since it avoids calling
49callback function.
50
51To get output from :type:`nghttp2_session` object, one can use
52`nghttp2_session_send()` or `nghttp2_session_mem_send()`.  The
53difference between them is that the former uses
54:type:`nghttp2_send_callback` to pass output to an application.  On
55the other hand, the latter returns the output to the caller.  If in
56doubt, use `nghttp2_session_mem_send()` since it is simpler.  But
57`nghttp2_session_send()` might be easier to use if the output buffer
58an application has is fixed sized.
59
60In general, an application should call `nghttp2_session_mem_send()`
61when it gets input from underlying connection.  Since there is great
62chance to get something pushed into transmission queue while the call
63of `nghttp2_session_mem_send()`, it is recommended to call
64`nghttp2_session_mem_recv()` after `nghttp2_session_mem_send()`.
65
66There is a question when we are safe to close HTTP/2 session without
67waiting for the closure of underlying connection.  We offer 2 API
68calls for this: `nghttp2_session_want_read()` and
69`nghttp2_session_want_write()`.  If they both return 0, application
70can destroy :type:`nghttp2_session`, and then close the underlying
71connection.  But make sure that the buffered output has been
72transmitted to the peer before closing the connection when
73`nghttp2_session_mem_send()` is used, since
74`nghttp2_session_want_write()` does not take into account the
75transmission of the buffered data outside of :type:`nghttp2_session`.
76
77Includes
78--------
79
80To use the public APIs, include ``nghttp2/nghttp2.h``::
81
82    #include <nghttp2/nghttp2.h>
83
84The header files are also available online: :doc:`nghttp2.h` and
85:doc:`nghttp2ver.h`.
86
87Remarks
88-------
89
90Do not call `nghttp2_session_send()`, `nghttp2_session_mem_send()`,
91`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` from the
92nghttp2 callback functions directly or indirectly. It will lead to the
93crash.  You can submit requests or frames in the callbacks then call
94these functions outside the callbacks.
95
96`nghttp2_session_send()` and `nghttp2_session_mem_send()` send first
9724 bytes of client magic string (MAGIC)
98(:macro:`NGHTTP2_CLIENT_MAGIC`) on client configuration.  The
99applications are responsible to send SETTINGS frame as part of
100connection preface using `nghttp2_submit_settings()`.  Similarly,
101`nghttp2_session_recv()` and `nghttp2_session_mem_recv()` consume
102MAGIC on server configuration unless
103`nghttp2_option_set_no_recv_client_magic()` is used with nonzero
104option value.
105
106.. _http-messaging:
107
108HTTP Messaging
109--------------
110
111By default, nghttp2 library checks HTTP messaging rules described in
112`HTTP/2 specification, section 8
113<https://tools.ietf.org/html/rfc7540#section-8>`_.  Everything
114described in that section is not validated however.  We briefly
115describe what the library does in this area.  In the following
116description, without loss of generality we omit CONTINUATION frame
117since they must follow HEADERS frame and are processed atomically.  In
118other words, they are just one big HEADERS frame.  To disable these
119validations, use `nghttp2_option_set_no_http_messaging()`.  Please
120note that disabling this feature does not change the fundamental
121client and server model of HTTP.  That is, even if the validation is
122disabled, only client can send requests.
123
124For HTTP request, including those carried by PUSH_PROMISE, HTTP
125message starts with one HEADERS frame containing request headers.  It
126is followed by zero or more DATA frames containing request body, which
127is followed by zero or one HEADERS containing trailer headers.  The
128request headers must include ":scheme", ":method" and ":path" pseudo
129header fields unless ":method" is not "CONNECT".  ":authority" is
130optional, but nghttp2 requires either ":authority" or "Host" header
131field must be present.  If ":method" is "CONNECT", the request headers
132must include ":method" and ":authority" and must omit ":scheme" and
133":path".
134
135For HTTP response, HTTP message starts with zero or more HEADERS
136frames containing non-final response (status code 1xx).  They are
137followed by one HEADERS frame containing final response headers
138(non-1xx).  It is followed by zero or more DATA frames containing
139response body, which is followed by zero or one HEADERS containing
140trailer headers.  The non-final and final response headers must
141contain ":status" pseudo header field containing 3 digits only.
142
143All request and response headers must include exactly one valid value
144for each pseudo header field.  Additionally nghttp2 requires all
145request headers must not include more than one "Host" header field.
146
147HTTP/2 prohibits connection-specific header fields.  The following
148header fields must not appear: "Connection", "Keep-Alive",
149"Proxy-Connection", "Transfer-Encoding" and "Upgrade".  Additionally,
150"TE" header field must not include any value other than "trailers".
151
152Each header field name and value must obey the field-name and
153field-value production rules described in `RFC 7230, section
1543.2. <https://tools.ietf.org/html/rfc7230#section-3.2>`_.
155Additionally, all field name must be lower cased.  The invalid header
156fields are treated as stream error, and that stream is reset.  If
157application wants to treat these headers in their own way, use
158`nghttp2_on_invalid_header_callback
159<https://nghttp2.org/documentation/types.html#c.nghttp2_on_invalid_header_callback>`_.
160
161For "http" or "https" URIs, ":path" pseudo header fields must start
162with "/".  The only exception is OPTIONS request, in that case, "*" is
163allowed in ":path" pseudo header field to represent system-wide
164OPTIONS request.
165
166With the above validations, nghttp2 library guarantees that header
167field name passed to `nghttp2_on_header_callback()` is not empty.
168Also required pseudo headers are all present and not empty.
169
170nghttp2 enforces "Content-Length" validation as well.  All request or
171response headers must not contain more than one "Content-Length"
172header field.  If "Content-Length" header field is present, it must be
173parsed as 64 bit signed integer.  The sum of data length in the
174following DATA frames must match with the number in "Content-Length"
175header field if it is present (this does not include padding bytes).
176
177RFC 7230 says that server must not send "Content-Length" in any
178response with 1xx, and 204 status code.  It also says that
179"Content-Length" is not allowed in any response with 200 status code
180to a CONNECT request.  nghttp2 enforces them as well.
181
182Any deviation results in stream error of type PROTOCOL_ERROR.  If
183error is found in PUSH_PROMISE frame, stream error is raised against
184promised stream.
185
186The order of transmission of the HTTP/2 frames
187----------------------------------------------
188
189This section describes the internals of libnghttp2 about the
190scheduling of transmission of HTTP/2 frames.  This is pretty much
191internal stuff, so the details could change in the future versions of
192the library.
193
194libnghttp2 categorizes HTTP/2 frames into 4 categories: urgent,
195regular, syn_stream, and data in the order of higher priority.
196
197The urgent category includes PING and SETTINGS.  They are sent with
198highest priority.  The order inside the category is FIFO.
199
200The regular category includes frames other than PING, SETTINGS, DATA,
201and HEADERS which does not create stream (which counts toward
202concurrent stream limit).  The order inside the category is FIFO.
203
204The syn_stream category includes HEADERS frame which creates stream,
205that counts toward the concurrent stream limit.
206
207The data category includes DATA frame, and the scheduling among DATA
208frames are determined by HTTP/2 dependency tree.
209
210If the application wants to send frames in the specific order, and the
211default transmission order does not fit, it has to schedule frames by
212itself using the callbacks (e.g.,
213:type:`nghttp2_on_frame_send_callback`).
214
215RST_STREAM has special side effect when it is submitted by
216`nghttp2_submit_rst_stream()`.  It cancels all pending HEADERS and
217DATA frames whose stream ID matches the one in the RST_STREAM frame.
218This may cause unexpected behaviour for the application in some cases.
219For example, suppose that application wants to send RST_STREAM after
220sending response HEADERS and DATA.  Because of the reason we mentioned
221above, the following code does not work:
222
223.. code-block:: c
224
225    nghttp2_submit_response(...)
226    nghttp2_submit_rst_stream(...)
227
228RST_STREAM cancels HEADERS (and DATA), and just RST_STREAM is sent.
229The correct way is use :type:`nghttp2_on_frame_send_callback`, and
230after HEADERS and DATA frames are sent, issue
231`nghttp2_submit_rst_stream()`.  FYI,
232:type:`nghttp2_on_frame_not_send_callback` tells you why frames are
233not sent.
234
235Implement user defined HTTP/2 non-critical extensions
236-----------------------------------------------------
237
238As of nghttp2 v1.8.0, we have added HTTP/2 non-critical extension
239framework, which lets application send and receive user defined custom
240HTTP/2 non-critical extension frames.  nghttp2 also offers built-in
241functionality to send and receive official HTTP/2 extension frames
242(e.g., ALTSVC frame).  For these built-in handler, refer to the next
243section.
244
245To send extension frame, use `nghttp2_submit_extension()`, and
246implement :type:`nghttp2_pack_extension_callback`.  The callback
247implements how to encode data into wire format.  The callback must be
248set to :type:`nghttp2_session_callbacks` using
249`nghttp2_session_callbacks_set_pack_extension_callback()`.
250
251For example, we will illustrate how to send `ALTSVC
252<https://tools.ietf.org/html/rfc7838>`_ frame.
253
254.. code-block:: c
255
256    typedef struct {
257      const char *origin;
258      const char *field;
259    } alt_svc;
260
261    ssize_t pack_extension_callback(nghttp2_session *session, uint8_t *buf,
262                                    size_t len, const nghttp2_frame *frame,
263                                    void *user_data) {
264      const alt_svc *altsvc = (const alt_svc *)frame->ext.payload;
265      size_t originlen = strlen(altsvc->origin);
266      size_t fieldlen = strlen(altsvc->field);
267
268      uint8_t *p;
269
270      if (len < 2 + originlen + fieldlen || originlen > 0xffff) {
271        return NGHTTP2_ERR_CANCEL;
272      }
273
274      p = buf;
275      *p++ = originlen >> 8;
276      *p++ = originlen & 0xff;
277      memcpy(p, altsvc->origin, originlen);
278      p += originlen;
279      memcpy(p, altsvc->field, fieldlen);
280      p += fieldlen;
281
282      return p - buf;
283    }
284
285This implements :type:`nghttp2_pack_extension_callback`.  We have to
286set this callback to :type:`nghttp2_session_callbacks`:
287
288.. code-block:: c
289
290    nghttp2_session_callbacks_set_pack_extension_callback(
291        callbacks, pack_extension_callback);
292
293To send ALTSVC frame, call `nghttp2_submit_extension()`:
294
295.. code-block:: c
296
297  static const alt_svc altsvc = {"example.com", "h2=\":8000\""};
298
299  nghttp2_submit_extension(session, 0xa, NGHTTP2_FLAG_NONE, 0,
300                           (void *)&altsvc);
301
302Notice that ALTSVC is use frame type ``0xa``.
303
304To receive extension frames, implement 2 callbacks:
305:type:`nghttp2_unpack_extension_callback` and
306:type:`nghttp2_on_extension_chunk_recv_callback`.
307:type:`nghttp2_unpack_extension_callback` implements the way how to
308decode wire format.  :type:`nghttp2_on_extension_chunk_recv_callback`
309implements how to buffer the incoming extension payload.  These
310callbacks must be set using
311`nghttp2_session_callbacks_set_unpack_extension_callback()` and
312`nghttp2_session_callbacks_set_on_extension_chunk_recv_callback()`
313respectively.  The application also must tell the library which
314extension frame type it is willing to receive using
315`nghttp2_option_set_user_recv_extension_type()`.  Note that the
316application has to create :type:`nghttp2_option` object for that
317purpose, and initialize session with it.
318
319We use ALTSVC again to illustrate how to receive extension frames.  We
320use different ``alt_svc`` struct than the previous one.
321
322First implement 2 callbacks.  We store incoming ALTSVC payload to
323global variable ``altsvc_buffer``.  Don't do this in production code
324since this is not thread safe:
325
326.. code-block:: c
327
328    typedef struct {
329      const uint8_t *origin;
330      size_t originlen;
331      const uint8_t *field;
332      size_t fieldlen;
333    } alt_svc;
334
335    /* buffers incoming ALTSVC payload */
336    uint8_t altsvc_buffer[4096];
337    /* The length of byte written to altsvc_buffer */
338    size_t altsvc_bufferlen = 0;
339
340    int on_extension_chunk_recv_callback(nghttp2_session *session,
341                                         const nghttp2_frame_hd *hd,
342                                         const uint8_t *data, size_t len,
343                                         void *user_data) {
344      if (sizeof(altsvc_buffer) < altsvc_bufferlen + len) {
345        altsvc_bufferlen = 0;
346        return NGHTTP2_ERR_CANCEL;
347      }
348
349      memcpy(altsvc_buffer + altsvc_bufferlen, data, len);
350      altsvc_bufferlen += len;
351
352      return 0;
353    }
354
355    int unpack_extension_callback(nghttp2_session *session, void **payload,
356                                  const nghttp2_frame_hd *hd, void *user_data) {
357      uint8_t *origin, *field;
358      size_t originlen, fieldlen;
359      uint8_t *p, *end;
360      alt_svc *altsvc;
361
362      if (altsvc_bufferlen < 2) {
363        altsvc_bufferlen = 0;
364        return NGHTTP2_ERR_CANCEL;
365      }
366
367      p = altsvc_buffer;
368      end = altsvc_buffer + altsvc_bufferlen;
369
370      originlen = ((*p) << 8) + *(p + 1);
371      p += 2;
372
373      if (p + originlen > end) {
374        altsvc_bufferlen = 0;
375        return NGHTTP2_ERR_CANCEL;
376      }
377
378      origin = p;
379      field = p + originlen;
380      fieldlen = end - field;
381
382      altsvc = (alt_svc *)malloc(sizeof(alt_svc));
383      altsvc->origin = origin;
384      altsvc->originlen = originlen;
385      altsvc->field = field;
386      altsvc->fieldlen = fieldlen;
387
388      *payload = altsvc;
389
390      altsvc_bufferlen = 0;
391
392      return 0;
393    }
394
395Set these callbacks to :type:`nghttp2_session_callbacks`:
396
397.. code-block:: c
398
399    nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
400        callbacks, on_extension_chunk_recv_callback);
401
402    nghttp2_session_callbacks_set_unpack_extension_callback(
403        callbacks, unpack_extension_callback);
404
405
406In ``unpack_extension_callback`` above, we set unpacked ``alt_svc``
407object to ``*payload``.  nghttp2 library then, calls
408:type:`nghttp2_on_frame_recv_callback`, and ``*payload`` will be
409available as ``frame->ext.payload``:
410
411.. code-block:: c
412
413    int on_frame_recv_callback(nghttp2_session *session,
414                               const nghttp2_frame *frame, void *user_data) {
415
416      switch (frame->hd.type) {
417      ...
418      case 0xa: {
419        alt_svc *altsvc = (alt_svc *)frame->ext.payload;
420        fprintf(stderr, "ALTSVC frame received\n");
421        fprintf(stderr, " origin: %.*s\n", (int)altsvc->originlen, altsvc->origin);
422        fprintf(stderr, " field : %.*s\n", (int)altsvc->fieldlen, altsvc->field);
423        free(altsvc);
424        break;
425      }
426      }
427
428      return 0;
429    }
430
431Finally, application should set the extension frame types it is
432willing to receive:
433
434.. code-block:: c
435
436    nghttp2_option_set_user_recv_extension_type(option, 0xa);
437
438The :type:`nghttp2_option` must be set to :type:`nghttp2_session` on
439its creation:
440
441.. code-block:: c
442
443    nghttp2_session_client_new2(&session, callbacks, user_data, option);
444
445How to use built-in HTTP/2 extension frame handlers
446---------------------------------------------------
447
448In the previous section, we talked about the user defined HTTP/2
449extension frames.  In this section, we talk about HTTP/2 extension
450frame support built into nghttp2 library.
451
452As of this writing, nghttp2 supports ALTSVC extension frame.  To send
453ALTSVC frame, use `nghttp2_submit_altsvc()` function.
454
455To receive ALTSVC frame through built-in functionality, application
456has to use `nghttp2_option_set_builtin_recv_extension_type()` to
457indicate the willingness of receiving ALTSVC frame:
458
459.. code-block:: c
460
461    nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC);
462
463This is very similar to the case when we used to receive user defined
464frames.
465
466If the same frame type is set using
467`nghttp2_option_set_builtin_recv_extension_type()` and
468`nghttp2_option_set_user_recv_extension_type()`, the latter takes
469precedence.  Application can implement its own frame handler rather
470than using built-in handler.
471
472The :type:`nghttp2_option` must be set to :type:`nghttp2_session` on
473its creation, like so:
474
475.. code-block:: c
476
477    nghttp2_session_client_new2(&session, callbacks, user_data, option);
478
479When ALTSVC is received, :type:`nghttp2_on_frame_recv_callback` will
480be called as usual.
481
482Stream priorities
483-----------------
484
485By default, the stream prioritization scheme described in :rfc:`7540`
486is used.  This scheme has been formally deprecated by :rfc:`9113`.  In
487order to disable it, send
488:enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` of
489value of 1 via `nghttp2_submit_settings()`.  This settings ID is
490defined by :rfc:`9218`.  The sender of this settings value disables
491RFC 7540 priorities, and instead it enables RFC 9218 Extensible
492Prioritization Scheme.  This new prioritization scheme has 2 methods
493to convey the stream priorities to a remote endpoint: Priority header
494field and PRIORITY_UPDATE frame.  nghttp2 supports both methods.  In
495order to receive and process PRIORITY_UPDATE frame, server has to call
496``nghttp2_option_set_builtin_recv_extension_type(option,
497NGHTTP2_PRIORITY_UPDATE)`` (see the above section), and pass the
498option to `nghttp2_session_server_new2()` or
499`nghttp2_session_server_new3()` to create a server session.  Client
500can send Priority header field via `nghttp2_submit_request()`.  It can
501also send PRIORITY_UPDATE frame via
502`nghttp2_submit_priority_update()`.  Server processes Priority header
503field in a request header field and updates the stream priority unless
504HTTP messaging rule enforcement is disabled (see
505`nghttp2_option_set_no_http_messaging()`).
506
507For the purpose of smooth migration from RFC 7540 priorities, client
508is advised to send
509:enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` of
510value of 1.  Until it receives the first server SETTINGS frame, it can
511send both RFC 7540 and RFC 9128 priority signals.  If client receives
512SETTINGS_NO_RFC7540_PRIORITIES of value of 0, or it is omitted ,
513client stops sending PRIORITY_UPDATE frame.  Priority header field
514will be sent in anyway since it is an end-to-end signal.  If
515SETTINGS_NO_RFC7540_PRIORITIES of value of 1 is received, client stops
516sending RFC 7540 priority signals.  This is the advice described in
517:rfc:`9218#section-2.1.1`.
518
519Server has an optional mechanism to fallback to RFC 7540 priorities.
520By default, if server sends SETTINGS_NO_RFC7540_PRIORITIES of value of
5211, it completely disables RFC 7540 priorities and no fallback.  By
522setting nonzero value to
523`nghttp2_option_set_server_fallback_rfc7540_priorities()`, server
524falls back to RFC 7540 priorities if it sends
525SETTINGS_NO_RFC7540_PRIORITIES value of value of 1, and client omits
526SETTINGS_NO_RFC7540_PRIORITIES in its SETTINGS frame.
527