src/http/ngx_http_priority.c - nginx-1.31.9 nginx/ @ 2a3a19c6f

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Nginx, Inc.
  3. */


  4. #include <ngx_config.h>
  5. #include <ngx_core.h>
  6. #include <ngx_http.h>


  7. ‌#define NGX_HTTP_PRIORITY_DEFAULT_URGENCY  3
  8. ‌#define NGX_HTTP_PRIORITY_URGENCY_MIN      0
  9. ‌#define NGX_HTTP_PRIORITY_URGENCY_MAX      7


  10. /* bare item types we distinguish; others are parsed and discarded */

  11. ‌#define NGX_HTTP_PRIORITY_INTEGER  0
  12. ‌#define NGX_HTTP_PRIORITY_BOOLEAN  1
  13. ‌#define NGX_HTTP_PRIORITY_OTHER    2


  14. /*
  15. * Parse limits.  RFC 8941 requires parsers to support at least these many
  16. * elements and characters; bounding the counts and individual item lengths
  17. * keeps parse time proportional to a well-formed field rather than to what a
  18. * peer chooses to send.
  19. */

  20. ‌#define NGX_HTTP_PRIORITY_MAX_MEMBERS      1024
  21. ‌#define NGX_HTTP_PRIORITY_MAX_INNER_ITEMS  256
  22. ‌#define NGX_HTTP_PRIORITY_MAX_PARAMS       256
  23. ‌#define NGX_HTTP_PRIORITY_MAX_STRING       1024
  24. ‌#define NGX_HTTP_PRIORITY_MAX_TOKEN        512
  25. ‌#define NGX_HTTP_PRIORITY_MAX_BINARY       21848  /* 16384 after decoding */


  26. static ngx_int_t ngx_http_priority_parse_key(u_char **pos, u_char *end,
  27.     ngx_str_t *key);
  28. static ngx_int_t ngx_http_priority_parse_member(u_char **pos, u_char *end,
  29.     ngx_str_t *key, ngx_int_t *type, ngx_int_t *value);
  30. static ngx_int_t ngx_http_priority_parse_value(u_char **pos, u_char *end,
  31.     ngx_int_t *type, ngx_int_t *value);
  32. static ngx_int_t ngx_http_priority_parse_inner_list(u_char **pos, u_char *end);
  33. static ngx_int_t ngx_http_priority_parse_bare_item(u_char **pos, u_char *end,
  34.     ngx_int_t *type, ngx_int_t *value);
  35. static ngx_int_t ngx_http_priority_parse_params(u_char **pos, u_char *end);
  36. static ngx_int_t ngx_http_priority_parse_number(u_char **pos, u_char *end,
  37.     ngx_int_t *type, ngx_int_t *value);
  38. static ngx_int_t ngx_http_priority_parse_string(u_char **pos, u_char *end);
  39. static ngx_int_t ngx_http_priority_parse_token(u_char **pos, u_char *end);
  40. static ngx_int_t ngx_http_priority_parse_binary(u_char **pos, u_char *end);


  41. void
  42. ‌ngx_http_priority_state_init(ngx_http_priority_state_t *ps)
  43. {
  44.     ngx_memzero(ps, sizeof(ngx_http_priority_state_t));

  45.     ps->client.urgency = NGX_HTTP_PRIORITY_DEFAULT_URGENCY;
  46.     ps->server.urgency = NGX_HTTP_PRIORITY_DEFAULT_URGENCY;
  47.     ps->effective.urgency = NGX_HTTP_PRIORITY_DEFAULT_URGENCY;
  48. }


  49. /*
  50. * Parse an RFC 9218 Priority field, a Structured Fields Dictionary
  51. * (RFC 8941).  The dictionary is parsed strictly: any syntax error fails
  52. * the whole field (RFC 8941, Section 4.2), and only then are the RFC 9218
  53. * rules applied, ignoring unknown members, out-of-range urgency and members
  54. * of an unexpected type.  On error the field is treated as absent and p is
  55. * left at defaults; callers keep any previously signalled priority.
  56. */

  57. ngx_int_t
  58. ‌ngx_http_priority_parse(ngx_str_t *field, ngx_http_priority_t *p)
  59. {
  60.     u_char      *last, *end;
  61.     ngx_str_t    key;
  62.     ngx_int_t    type, value;
  63.     ngx_uint_t   members;

  64.     p->urgency = NGX_HTTP_PRIORITY_DEFAULT_URGENCY;
  65.     p->incremental = 0;
  66.     p->urgency_set = 0;
  67.     p->incremental_set = 0;

  68.     if (field == NULL) {
  69.         return NGX_OK;
  70.     }

  71.     /* RFC 8941, Section 4.2.2 */

  72.     last = field->data;
  73.     end = last + field->len;
  74.     members = 0;

  75.     /* Section 4.2, step 2: discard leading SP */

  76.     while (last < end && *last == ' ') {
  77.         last++;
  78.     }

  79.     while (last < end) {

  80.         if (++members > NGX_HTTP_PRIORITY_MAX_MEMBERS) {
  81.             return NGX_ERROR;
  82.         }

  83.         if (ngx_http_priority_parse_member(&last, end, &key, &type, &value)
  84.             != NGX_OK)
  85.         {
  86.             return NGX_ERROR;
  87.         }

  88.         /*
  89.          * RFC 8941, Section 4.2.2, step 2.4: a duplicate key replaces the
  90.          * earlier member.  RFC 9218, Section 4: unknown members and members
  91.          * of an unexpected type are ignored.  A repeated u or i therefore
  92.          * resets to the default first, so a later out-of-range or
  93.          * wrong-typed value leaves the parameter absent rather than keeping
  94.          * the earlier one.
  95.          */

  96.         if (key.len == 1 && key.data[0] == 'u') {

  97.             p->urgency = NGX_HTTP_PRIORITY_DEFAULT_URGENCY;
  98.             p->urgency_set = 0;

  99.             if (type == NGX_HTTP_PRIORITY_INTEGER
  100.                 && value >= NGX_HTTP_PRIORITY_URGENCY_MIN
  101.                 && value <= NGX_HTTP_PRIORITY_URGENCY_MAX)
  102.             {
  103.                 p->urgency = (unsigned) value;
  104.                 p->urgency_set = 1;
  105.             }

  106.         } else if (key.len == 1 && key.data[0] == 'i') {

  107.             p->incremental = 0;
  108.             p->incremental_set = 0;

  109.             if (type == NGX_HTTP_PRIORITY_BOOLEAN) {
  110.                 p->incremental = (unsigned) value;
  111.                 p->incremental_set = 1;
  112.             }
  113.         }

  114.         while (last < end && (*last == ' ' || *last == '\t')) {
  115.             last++;
  116.         }

  117.         if (last == end) {
  118.             break;
  119.         }

  120.         if (*last++ != ',') {
  121.             return NGX_ERROR;
  122.         }

  123.         while (last < end && (*last == ' ' || *last == '\t')) {
  124.             last++;
  125.         }

  126.         /* trailing comma */

  127.         if (last == end) {
  128.             return NGX_ERROR;
  129.         }
  130.     }

  131.     return NGX_OK;
  132. }


  133. /*
  134. * Parse one dictionary member and its parameters (RFC 8941, Section 4.2.2).
  135. * A bare key (no "=") is a Boolean true; type/value then describe the
  136. * member's value so the caller can pick out u and i and ignore the rest.
  137. */

  138. static ngx_int_t
  139. ‌ngx_http_priority_parse_member(u_char **pos, u_char *end, ngx_str_t *key,
  140.     ngx_int_t *type, ngx_int_t *value)
  141. {
  142.     u_char  *last;

  143.     last = *pos;

  144.     if (ngx_http_priority_parse_key(&last, end, key) != NGX_OK) {
  145.         return NGX_ERROR;
  146.     }

  147.     if (last < end && *last == '=') {
  148.         last++;

  149.         if (ngx_http_priority_parse_value(&last, end, type, value) != NGX_OK) {
  150.             return NGX_ERROR;
  151.         }

  152.     } else {
  153.         *type = NGX_HTTP_PRIORITY_BOOLEAN;
  154.         *value = 1;

  155.         if (ngx_http_priority_parse_params(&last, end) != NGX_OK) {
  156.             return NGX_ERROR;
  157.         }
  158.     }

  159.     *pos = last;

  160.     return NGX_OK;
  161. }


  162. /* RFC 8941, Section 4.2.1.1: an Item or an Inner List, each with Parameters */

  163. static ngx_int_t
  164. ‌ngx_http_priority_parse_value(u_char **pos, u_char *end, ngx_int_t *type,
  165.     ngx_int_t *value)
  166. {
  167.     u_char  *last;

  168.     last = *pos;

  169.     if (last < end && *last == '(') {
  170.         *type = NGX_HTTP_PRIORITY_OTHER;
  171.         *value = 0;

  172.         if (ngx_http_priority_parse_inner_list(&last, end) != NGX_OK) {
  173.             return NGX_ERROR;
  174.         }

  175.     } else {
  176.         if (ngx_http_priority_parse_bare_item(&last, end, type, value)
  177.             != NGX_OK)
  178.         {
  179.             return NGX_ERROR;
  180.         }
  181.     }

  182.     if (ngx_http_priority_parse_params(&last, end) != NGX_OK) {
  183.         return NGX_ERROR;
  184.     }

  185.     *pos = last;

  186.     return NGX_OK;
  187. }


  188. /* RFC 8941, Section 4.2.3.3 */

  189. static ngx_int_t
  190. ‌ngx_http_priority_parse_key(u_char **pos, u_char *end, ngx_str_t *key)
  191. {
  192.     u_char  *last, ch;

  193.     last = *pos;

  194.     if (last == end) {
  195.         return NGX_ERROR;
  196.     }

  197.     ch = *last;

  198.     if (!((ch >= 'a' && ch <= 'z') || ch == '*')) {
  199.         return NGX_ERROR;
  200.     }

  201.     key->data = last;

  202.     do {
  203.         last++;

  204.         if (last == end) {
  205.             break;
  206.         }

  207.         ch = *last;

  208.     } while ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')
  209.              || ch == '_' || ch == '-' || ch == '.' || ch == '*');

  210.     key->len = last - *pos;
  211.     *pos = last;

  212.     return NGX_OK;
  213. }


  214. /* RFC 8941, Section 4.2.3.2 */

  215. static ngx_int_t
  216. ‌ngx_http_priority_parse_params(u_char **pos, u_char *end)
  217. {
  218.     u_char     *last;
  219.     ngx_str_t   key;
  220.     ngx_int_t   type, value;
  221.     ngx_uint_t  params;

  222.     last = *pos;
  223.     params = 0;

  224.     while (last < end && *last == ';') {
  225.         last++;

  226.         if (++params > NGX_HTTP_PRIORITY_MAX_PARAMS) {
  227.             return NGX_ERROR;
  228.         }

  229.         /* Section 4.2.3.2, step 2.3: discard leading SP */

  230.         while (last < end && *last == ' ') {
  231.             last++;
  232.         }

  233.         if (ngx_http_priority_parse_key(&last, end, &key) != NGX_OK) {
  234.             return NGX_ERROR;
  235.         }

  236.         if (last < end && *last == '=') {
  237.             last++;

  238.             if (ngx_http_priority_parse_bare_item(&last, end, &type, &value)
  239.                 != NGX_OK)
  240.             {
  241.                 return NGX_ERROR;
  242.             }
  243.         }
  244.     }

  245.     *pos = last;

  246.     return NGX_OK;
  247. }


  248. /* RFC 8941, Section 4.2.1.2: Inner List */

  249. static ngx_int_t
  250. ‌ngx_http_priority_parse_inner_list(u_char **pos, u_char *end)
  251. {
  252.     u_char     *last;
  253.     ngx_int_t   type, value;
  254.     ngx_uint_t  items;

  255.     last = *pos + 1;
  256.     items = 0;

  257.     for ( ;; ) {

  258.         while (last < end && *last == ' ') {
  259.             last++;
  260.         }

  261.         if (last < end && *last == ')') {
  262.             *pos = last + 1;
  263.             return NGX_OK;
  264.         }

  265.         if (++items > NGX_HTTP_PRIORITY_MAX_INNER_ITEMS) {
  266.             return NGX_ERROR;
  267.         }

  268.         if (ngx_http_priority_parse_bare_item(&last, end, &type, &value)
  269.             != NGX_OK)
  270.         {
  271.             return NGX_ERROR;
  272.         }

  273.         if (ngx_http_priority_parse_params(&last, end) != NGX_OK) {
  274.             return NGX_ERROR;
  275.         }

  276.         if (last == end) {
  277.             return NGX_ERROR;
  278.         }

  279.         if (*last != ' ' && *last != ')') {
  280.             return NGX_ERROR;
  281.         }
  282.     }
  283. }


  284. /* RFC 8941, Section 4.2.3.1 */

  285. static ngx_int_t
  286. ‌ngx_http_priority_parse_bare_item(u_char **pos, u_char *end, ngx_int_t *type,
  287.     ngx_int_t *value)
  288. {
  289.     u_char  ch;

  290.     if (*pos == end) {
  291.         return NGX_ERROR;
  292.     }

  293.     ch = **pos;

  294.     if (ch == '-' || (ch >= '0' && ch <= '9')) {
  295.         return ngx_http_priority_parse_number(pos, end, type, value);
  296.     }

  297.     if (ch == '?') {
  298.         (*pos)++;

  299.         if (*pos == end || (**pos != '0' && **pos != '1')) {
  300.             return NGX_ERROR;
  301.         }

  302.         *type = NGX_HTTP_PRIORITY_BOOLEAN;
  303.         *value = (**pos == '1');
  304.         (*pos)++;

  305.         return NGX_OK;
  306.     }

  307.     *type = NGX_HTTP_PRIORITY_OTHER;

  308.     if (ch == '"') {
  309.         return ngx_http_priority_parse_string(pos, end);
  310.     }

  311.     if (ch == ':') {
  312.         return ngx_http_priority_parse_binary(pos, end);
  313.     }

  314.     if (((ch | 0x20) >= 'a' && (ch | 0x20) <= 'z') || ch == '*') {
  315.         return ngx_http_priority_parse_token(pos, end);
  316.     }

  317.     return NGX_ERROR;
  318. }


  319. /* RFC 8941, Section 4.2.4: Integer or Decimal */

  320. static ngx_int_t
  321. ‌ngx_http_priority_parse_number(u_char **pos, u_char *end, ngx_int_t *type,
  322.     ngx_int_t *value)
  323. {
  324.     u_char     *last, *start;
  325.     ngx_int_t   n;
  326.     ngx_uint_t  neg, decimal, digits, frac;

  327.     last = *pos;
  328.     neg = 0;

  329.     if (*last == '-') {
  330.         neg = 1;
  331.         last++;
  332.     }

  333.     start = last;
  334.     decimal = 0;
  335.     digits = 0;
  336.     frac = 0;

  337.     /* RFC 8941, Section 4.2.4 */

  338.     while (last < end) {

  339.         if (*last >= '0' && *last <= '9') {
  340.             digits++;

  341.             /* step 7.5: an Integer has at most 15 digits */

  342.             if (!decimal && digits > 15) {
  343.                 return NGX_ERROR;
  344.             }

  345.             /* step 9.2: a Decimal has at most three fractional digits */

  346.             if (decimal && ++frac > 3) {
  347.                 return NGX_ERROR;
  348.             }

  349.             last++;
  350.             continue;
  351.         }

  352.         /* step 8: a single "." starts the fractional part of a Decimal */

  353.         if (*last == '.' && !decimal) {

  354.             /* step 8.1: at most 12 digits before the decimal point */

  355.             if (digits == 0 || digits > 12) {
  356.                 return NGX_ERROR;
  357.             }

  358.             decimal = 1;
  359.             last++;
  360.             continue;
  361.         }

  362.         break;
  363.     }

  364.     if (digits == 0) {
  365.         return NGX_ERROR;
  366.     }

  367.     if (decimal) {

  368.         /* step 9.1: a Decimal must not end with "." */

  369.         if (frac == 0) {
  370.             return NGX_ERROR;
  371.         }

  372.         /* a valid Decimal, but not an Integer: parsed and discarded */

  373.         *type = NGX_HTTP_PRIORITY_OTHER;
  374.         *value = 0;
  375.         *pos = last;
  376.         return NGX_OK;
  377.     }

  378.     n = ngx_atoi(start, last - start);
  379.     if (n == NGX_ERROR) {

  380.         /* a valid Integer exceeding ngx_int_t: parsed and discarded */

  381.         *type = NGX_HTTP_PRIORITY_OTHER;
  382.         *value = 0;
  383.         *pos = last;
  384.         return NGX_OK;
  385.     }

  386.     *type = NGX_HTTP_PRIORITY_INTEGER;
  387.     *value = neg ? -n : n;
  388.     *pos = last;

  389.     return NGX_OK;
  390. }


  391. /* RFC 8941, Section 4.2.5: String */

  392. static ngx_int_t
  393. ‌ngx_http_priority_parse_string(u_char **pos, u_char *end)
  394. {
  395.     u_char      *last, ch;
  396.     ngx_uint_t   len;

  397.     last = *pos + 1;
  398.     len = 0;

  399.     while (last < end) {
  400.         ch = *last++;

  401.         if (ch == '\\') {
  402.             if (last == end || (*last != '"' && *last != '\\')) {
  403.                 return NGX_ERROR;
  404.             }
  405.             last++;

  406.             if (++len > NGX_HTTP_PRIORITY_MAX_STRING) {
  407.                 return NGX_ERROR;
  408.             }

  409.             continue;
  410.         }

  411.         if (ch == '"') {
  412.             *pos = last;
  413.             return NGX_OK;
  414.         }

  415.         if (ch < 0x20 || ch >= 0x7f) {
  416.             return NGX_ERROR;
  417.         }

  418.         if (++len > NGX_HTTP_PRIORITY_MAX_STRING) {
  419.             return NGX_ERROR;
  420.         }
  421.     }

  422.     return NGX_ERROR;
  423. }


  424. /* RFC 8941, Section 4.2.6: Token */

  425. static ngx_int_t
  426. ‌ngx_http_priority_parse_token(u_char **pos, u_char *end)
  427. {
  428.     u_char  *last, ch;

  429.     last = *pos + 1;

  430.     while (last < end) {
  431.         ch = *last;

  432.         if (ch == ':' || ch == '/'
  433.             || ((ch | 0x20) >= 'a' && (ch | 0x20) <= 'z')
  434.             || (ch >= '0' && ch <= '9')
  435.             || ch == '!' || ch == '#' || ch == '$' || ch == '%'
  436.             || ch == '&' || ch == '\'' || ch == '*' || ch == '+'
  437.             || ch == '-' || ch == '.' || ch == '^' || ch == '_'
  438.             || ch == '`' || ch == '|' || ch == '~')
  439.         {
  440.             if ((ngx_uint_t) (last - *pos) >= NGX_HTTP_PRIORITY_MAX_TOKEN) {
  441.                 return NGX_ERROR;
  442.             }

  443.             last++;
  444.             continue;
  445.         }

  446.         break;
  447.     }

  448.     *pos = last;

  449.     return NGX_OK;
  450. }


  451. /* RFC 8941, Section 4.2.7: Byte Sequence */

  452. static ngx_int_t
  453. ‌ngx_http_priority_parse_binary(u_char **pos, u_char *end)
  454. {
  455.     u_char      *last, ch;
  456.     ngx_uint_t   len;

  457.     last = *pos + 1;
  458.     len = 0;

  459.     while (last < end) {
  460.         ch = *last++;

  461.         if (ch == ':') {

  462.             /* the content has to decode: 4n + 1 characters cannot */

  463.             if (len % 4 == 1) {
  464.                 return NGX_ERROR;
  465.             }

  466.             *pos = last;
  467.             return NGX_OK;
  468.         }

  469.         if (!(((ch | 0x20) >= 'a' && (ch | 0x20) <= 'z')
  470.               || (ch >= '0' && ch <= '9')
  471.               || ch == '+' || ch == '/' || ch == '='))
  472.         {
  473.             return NGX_ERROR;
  474.         }

  475.         if (++len > NGX_HTTP_PRIORITY_MAX_BINARY) {
  476.             return NGX_ERROR;
  477.         }
  478.     }

  479.     return NGX_ERROR;
  480. }


  481. void
  482. ‌ngx_http_priority_state_update(ngx_http_priority_state_t *ps)
  483. {
  484.     ngx_http_priority_t  *e;

  485.     e = &ps->effective;
  486.     *e = ps->client;

  487.     if (ps->server.urgency_set) {
  488.         e->urgency = ps->server.urgency;
  489.         e->urgency_set = 1;
  490.     }

  491.     if (ps->server.incremental_set) {
  492.         e->incremental = ps->server.incremental;
  493.         e->incremental_set = 1;
  494.     }
  495. }