libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

h2_req_fields.c (17103B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2025 Evgeny Grin (Karlson2k)
      5 
      6   GNU libmicrohttpd is free software; you can redistribute it and/or
      7   modify it under the terms of the GNU Lesser General Public
      8   License as published by the Free Software Foundation; either
      9   version 2.1 of the License, or (at your option) any later version.
     10 
     11   GNU libmicrohttpd is distributed in the hope that it will be useful,
     12   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14   Lesser General Public License for more details.
     15 
     16   Alternatively, you can redistribute GNU libmicrohttpd and/or
     17   modify it under the terms of the GNU General Public License as
     18   published by the Free Software Foundation; either version 2 of
     19   the License, or (at your option) any later version, together
     20   with the eCos exception, as follows:
     21 
     22     As a special exception, if other files instantiate templates or
     23     use macros or inline functions from this file, or you compile this
     24     file and link it with other works to produce a work based on this
     25     file, this file does not by itself cause the resulting work to be
     26     covered by the GNU General Public License. However the source code
     27     for this file must still be made available in accordance with
     28     section (3) of the GNU General Public License v2.
     29 
     30     This exception does not invalidate any other reasons why a work
     31     based on this file might be covered by the GNU General Public
     32     License.
     33 
     34   You should have received copies of the GNU Lesser General Public
     35   License and the GNU General Public License along with this library;
     36   if not, see <https://www.gnu.org/licenses/>.
     37 */
     38 
     39 /**
     40  * @file src/mhd2/h2/h2_dec_fields.c
     41  * @brief  Implementation of HTTP/2 request fields functions
     42  * @author Karlson2k (Evgeny Grin)
     43  */
     44 
     45 #include "mhd_sys_options.h"
     46 
     47 #include "sys_bool_type.h"
     48 #include "sys_base_types.h"
     49 
     50 #include <string.h>
     51 
     52 #include "mhd_assert.h"
     53 #include "mhd_unreachable.h"
     54 #include "mhd_assume.h"
     55 
     56 #include "mhd_constexpr.h"
     57 #include "mhd_buffer.h"
     58 #include "mhd_str_types.h"
     59 #include "mhd_str_macros.h"
     60 
     61 #include "mhd_connection.h"
     62 #include "h2_conn_data.h"
     63 
     64 #include "h2_stream_data.h"
     65 
     66 #include "mhd_str.h"
     67 
     68 #include "stream_process_request.h"
     69 
     70 #include "h2_req_item_kinds.h"
     71 #include "h2_req_item_struct.h"
     72 #include "h2_req_items_funcs.h"
     73 
     74 #include "hpack/mhd_hpack_codec.h"
     75 
     76 #include "h2_proc_conn.h"
     77 #include "h2_conn_streams.h"
     78 
     79 #include "h2_req_fields.h"
     80 
     81 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
     82 MHD_FN_PAR_INOUT_ (1) MHD_FN_PAR_IN_ (2)
     83 MHD_FN_PAR_INOUT_ (4) MHD_FN_PAR_OUT_ (5) enum mhd_H2DecFieldsResult
     84 mhd_h2_req_fields_decode (struct mhd_HpackDecContext *restrict hk_dec,
     85                           const struct mhd_Buffer *restrict enc_data,
     86                           bool are_trailers,
     87                           struct mhd_H2ReqItemsBlock *restrict ib,
     88                           size_t *restrict left_unprocessed)
     89 {
     90   mhd_constexpr unsigned int max_no_fields = 8u;
     91   const enum mhd_H2RequestItemKind field_kind =
     92     are_trailers ? mhd_H2_RIK_TRAILER : mhd_H2_RIK_HEADER;
     93   size_t pos;
     94   unsigned int no_fields;
     95   enum mhd_H2DecFieldsResult ret;
     96 
     97   pos = 0u;
     98   no_fields = 0u;
     99   ret = mhd_H2_DEC_FIELDS_OK;
    100 
    101   while (pos < enc_data->size)
    102   {
    103     size_t name_len;
    104     size_t val_len;
    105     size_t pos_incr;
    106     enum mhd_HpackDecResult res;
    107     struct mhd_Buffer buff;
    108 
    109     mhd_assert (mhd_H2_DEC_FIELDS_OK == ret);
    110 
    111     if (! mhd_h2_items_get_buff_new_item (ib,
    112                                           &buff))
    113       return mhd_H2_DEC_FIELDS_NO_SPACE;
    114 
    115     res = mhd_hpack_dec_data (hk_dec,
    116                               enc_data->size - pos,
    117                               (const uint8_t *) enc_data->data + pos,
    118                               buff.size,
    119                               buff.data,
    120                               &name_len,
    121                               &val_len,
    122                               &pos_incr);
    123 
    124     if (! mhd_HPACK_DEC_RES_IS_ERR (res))
    125     {
    126       mhd_assert (0u != pos_incr);
    127       pos += pos_incr;
    128     }
    129 
    130     switch (res)
    131     {
    132     case mhd_HPACK_DEC_RES_NO_NEW_FIELD:
    133       if (max_no_fields < ++no_fields)
    134       {
    135         ret = mhd_H2_DEC_FIELDS_PROT_ABUSE;
    136         break;
    137       }
    138       mhd_h2_items_cancel_new_item_buff (ib);
    139       continue;
    140     case mhd_HPACK_DEC_RES_NEW_FIELD:
    141       mhd_assert (buff.size >= (name_len + val_len + 2u));
    142       mhd_assert (0 == buff.data[name_len]);
    143       mhd_assert (0 == buff.data[name_len + 1 + val_len]);
    144       mhd_h2_items_add_new_item_buff (ib,
    145                                       name_len,
    146                                       val_len,
    147                                       (':' == buff.data[0]) ?
    148                                       mhd_H2_RIK_PSEUDOHEADER : field_kind);
    149       continue;
    150     case mhd_HPACK_DEC_RES_INCOMPLETE:
    151       mhd_assert (mhd_H2_DEC_FIELDS_OK == ret);
    152       break;
    153     case mhd_HPACK_DEC_RES_ALLOC_ERR:
    154       ret = mhd_H2_DEC_FIELDS_INT_ERR;
    155       break;
    156     case mhd_HPACK_DEC_RES_BUFFER_TOO_SMALL: // TODO: support "minimal" decoding for decoder state updates
    157     case mhd_HPACK_DEC_RES_STRING_TOO_LONG:
    158       ret = mhd_H2_DEC_FIELDS_NO_SPACE;
    159       break;
    160     case mhd_HPACK_DEC_RES_NUMBER_TOO_LONG:
    161     case mhd_HPACK_DEC_RES_DYN_SIZE_UPD_TOO_LARGE:
    162       ret = mhd_H2_DEC_FIELDS_PROT_ABUSE;
    163       break;
    164     case mhd_HPACK_DEC_RES_DYN_SIZE_UPD_MISSING:
    165     case mhd_HPACK_DEC_RES_HUFFMAN_ERR:
    166     case mhd_HPACK_DEC_RES_HPACK_BAD_IDX:
    167     case mhd_HPACK_DEC_RES_HPACK_ERR:
    168       ret = mhd_H2_DEC_FIELDS_BROKEN_DATA;
    169       break;
    170     case mhd_HPACK_DEC_RES_INTERNAL_ERR:
    171     default:
    172       mhd_UNREACHABLE_D ("Impossible value");
    173       ret = mhd_H2_DEC_FIELDS_INT_ERR;
    174       break;
    175     }
    176     mhd_h2_items_cancel_new_item_buff (ib);
    177     break; /* Break the loop */
    178   }
    179 
    180   mhd_assert (pos <= enc_data->size);
    181   *left_unprocessed = enc_data->size - pos;
    182 
    183   return ret;
    184 }
    185 
    186 
    187 static inline MHD_FN_PAR_NONNULL_ALL_
    188 MHD_FN_PAR_INOUT_ (1) bool
    189 req_validate_fields_chars (struct mhd_H2Stream *restrict s)
    190 {
    191   // TODO: implement checking all field chars for validity, RFC 9113 Section 8.2.1
    192   (void) s;
    193   return true;
    194 }
    195 
    196 
    197 static inline MHD_FN_PAR_NONNULL_ALL_
    198 MHD_FN_PAR_INOUT_ (1)
    199 MHD_FN_PAR_OUT_ (2) bool
    200 req_pseudoheaders_preprocess (struct mhd_H2Stream *restrict s,
    201                               size_t *pos)
    202 {
    203   static const struct MHD_String ph_method = mhd_MSTR_INIT (":method");
    204   static const struct MHD_String ph_scheme = mhd_MSTR_INIT (":scheme");
    205   static const struct MHD_String ph_authority = mhd_MSTR_INIT (":authority");
    206   static const struct MHD_String ph_path = mhd_MSTR_INIT (":path");
    207   struct mhd_H2ReqItemsBlock *const ib = s->c->h2.mem.req_ib;
    208   const char *buff = mhd_h2_items_get_strings_buffc (ib);
    209   bool have_method = false;
    210   bool have_scheme = false;
    211   bool have_authority = false;
    212   bool have_path = false;
    213 
    214   *pos = 0u;
    215   while (1)
    216   {
    217     const struct mhd_H2ReqItem *item;
    218     item = mhd_h2_items_get_item_nc (ib,
    219                                      *pos);
    220 
    221     if (NULL == item)
    222       break;
    223     else if (mhd_H2_RIK_PSEUDOHEADER != item->kind)
    224       break;
    225     else if ((item->name_len == ph_method.len) &&
    226              (0 == memcmp (buff + item->offset,
    227                            ph_method.cstr,
    228                            ph_method.len)))
    229     {
    230       if (have_method)
    231         return mhd_h2_stream_req_problem (s,
    232                                           mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP);
    233       have_method = true;
    234       s->req.pos_method = *pos;
    235       s->req.method =
    236         mhd_parse_http_method (item->val_len,
    237                                buff + item->offset + ph_method.len + 1u);
    238     }
    239     else if ((item->name_len == ph_path.len) &&
    240              (0 == memcmp (buff + item->offset,
    241                            ph_path.cstr,
    242                            ph_path.len)))
    243     {
    244       if (have_path)
    245         return mhd_h2_stream_req_problem (s,
    246                                           mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP);
    247       have_path = true;
    248       s->req.pos_path = *pos;
    249     }
    250     else if ((item->name_len == ph_authority.len) &&
    251              (0 == memcmp (buff + item->offset,
    252                            ph_authority.cstr,
    253                            ph_authority.len)))
    254     {
    255       if (have_authority)
    256         return mhd_h2_stream_req_problem (s,
    257                                           mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP);
    258       have_authority = true;
    259       s->req.pos_authority = *pos;
    260     }
    261     else if ((item->name_len == ph_scheme.len) &&
    262              (0 == memcmp (buff + item->offset,
    263                            ph_scheme.cstr,
    264                            ph_scheme.len)))
    265     {
    266       if (have_scheme)
    267         return mhd_h2_stream_req_problem (s,
    268                                           mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP);
    269       have_scheme = true;
    270     }
    271     mhd_assert (':' == buff[item->offset]);
    272     ++(*pos);
    273   }
    274 
    275   if (! have_method)
    276     return mhd_h2_stream_req_problem (s,
    277                                       mhd_H2_REQ_PRBLM_PSEUDOHDR_MISSING);
    278 
    279   if (mhd_HTTP_METHOD_CONNECT != s->req.method)
    280   {
    281     if (! have_path || ! have_scheme)
    282       return mhd_h2_stream_req_problem (s,
    283                                         mhd_H2_REQ_PRBLM_PSEUDOHDR_EXTRA);
    284   }
    285   else
    286   {
    287     if (have_path || have_scheme)
    288       return mhd_h2_stream_req_problem (s,
    289                                         mhd_H2_REQ_PRBLM_PSEUDOHDR_MISSING);
    290   }
    291 
    292   return true;
    293 }
    294 
    295 
    296 static inline MHD_FN_PAR_NONNULL_ALL_
    297 MHD_FN_PAR_INOUT_ (1)
    298 MHD_FN_PAR_OUT_ (2) bool
    299 req_headers_preprocess (struct mhd_H2Stream *restrict s,
    300                         size_t *pos)
    301 {
    302   static const struct MHD_String h_host = mhd_MSTR_INIT ("host");
    303   static const struct MHD_String h_cntn_len = mhd_MSTR_INIT ("content-length");
    304   struct mhd_H2ReqItemsBlock *const ib = s->c->h2.mem.req_ib;
    305   const char *buff = mhd_h2_items_get_strings_buffc (ib);
    306 
    307   while (1)
    308   {
    309     const struct mhd_H2ReqItem *item;
    310     item = mhd_h2_items_get_item_nc (ib,
    311                                      *pos);
    312 
    313     if (NULL == item)
    314       break;
    315     else if (mhd_H2_RIK_PSEUDOHEADER == item->kind)
    316       return mhd_h2_stream_req_problem (s,
    317                                         mhd_H2_REQ_PRBLM_PSEUDOHDR_AFTER_HDR);
    318     else if (mhd_H2_RIK_HEADER != item->kind)
    319       (void) 0; /* skip */
    320     else if ((item->name_len == h_cntn_len.len) &&
    321              (0 == memcmp (buff + item->offset,
    322                            h_cntn_len.cstr,
    323                            h_cntn_len.len)))
    324     {
    325       uint_fast64_t cntnt_len;
    326 
    327       if ((0u == item->val_len) ||
    328           (item->val_len !=
    329            mhd_str_to_uint64 (buff + item->offset + h_cntn_len.len + 1u,
    330                               &cntnt_len)))
    331         return mhd_h2_stream_req_problem (s,
    332                                           mhd_H2_REQ_PRBLM_CNTNT_LEN_WRONG);
    333 
    334       if ((MHD_SIZE_UNKNOWN != s->req.cntn_size) &&
    335           (s->req.cntn_size != cntnt_len))
    336         return mhd_h2_stream_req_problem (s,
    337                                           mhd_H2_REQ_PRBLM_CNTNT_LEN_WRONG);
    338       else
    339         s->req.cntn_size = cntnt_len;
    340     }
    341     else if ((item->name_len == h_host.len) &&
    342              (0 == memcmp (buff + item->offset,
    343                            h_host.cstr,
    344                            h_host.len)))
    345     {
    346       if (mhd_H2_REQ_ITEM_POS_INVALID == s->req.pos_authority)
    347         s->req.pos_authority = *pos;
    348       else
    349       {
    350         const struct mhd_H2ReqItem *item_auth =
    351           mhd_h2_items_get_item_nc (ib,
    352                                     s->req.pos_authority);
    353         mhd_assert (NULL != item_auth);
    354         if ((item_auth->val_len != item->val_len) ||
    355             (! mhd_str_equal_caseless_bin_n (buff + item->offset
    356                                              + item->name_len + 1u,
    357                                              buff + item_auth->offset
    358                                              + item_auth->name_len + 1u,
    359                                              item->val_len)))
    360           return
    361             mhd_h2_stream_req_problem (s,
    362                                        mhd_H2_REQ_PRBLM_HOST_HDR_WRONG_EXTRA);
    363       }
    364     }
    365 
    366     ++(*pos);
    367     mhd_assert ((mhd_H2_RIK_HEADER != item->kind) ||
    368                 (':' != buff[item->offset]));
    369   }
    370 
    371   return true;
    372 }
    373 
    374 
    375 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    376 MHD_FN_PAR_INOUT_ (1) bool
    377 mhd_h2_req_headers_preprocess (struct mhd_H2Stream *restrict s)
    378 {
    379   size_t pos;
    380 
    381   mhd_assert (mhd_h2_items_debug_get_streamid (s->c->h2.mem.req_ib)
    382               == s->stream_id);
    383 
    384   if (! req_validate_fields_chars (s))
    385     return false;
    386 
    387   if (! req_pseudoheaders_preprocess (s,
    388                                       &pos))
    389     return false;
    390 
    391   mhd_assert (0u != pos);
    392   mhd_assert (mhd_HTTP_METHOD_NO_METHOD != s->req.method);
    393 
    394   if (! req_headers_preprocess (s,
    395                                 &pos))
    396     return false;
    397 
    398   return true;
    399 }
    400 
    401 
    402 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    403 MHD_FN_PAR_INOUT_ (1) bool
    404 mhd_h2_req_uri_parse (struct mhd_H2Stream *restrict s)
    405 {
    406   struct mhd_H2ReqItemsBlock *const restrict ib = s->c->h2.mem.req_ib;
    407   char *const restrict buff = mhd_h2_items_get_strings_buff (ib);
    408   struct mhd_H2ReqItem *restrict item =
    409     mhd_h2_items_get_item_n (ib,
    410                              s->req.pos_path);
    411   const size_t path_start = (size_t) (item->offset + item->name_len + 1u);
    412   char *questn_mark;
    413 
    414   questn_mark = (char*) memchr (buff + path_start,
    415                                 '?',
    416                                 (size_t) item->val_len);
    417   if (NULL == questn_mark)
    418   {
    419     item->val_len =
    420       (uint_least32_t)
    421       mhd_str_dec_norm_uri_path ((size_t) item->val_len,
    422                                  buff + path_start);
    423   }
    424   else
    425   {
    426     const size_t path_len = (size_t) (questn_mark - (buff + path_start));
    427     const size_t uri_end = (size_t) (path_start + item->val_len);
    428     size_t i = path_start + path_len + 1u;
    429 
    430     mhd_assert (path_len < item->val_len);
    431 
    432     buff[path_start + path_len] = '\0'; /* Zero-terminate the path */
    433     item->val_len =
    434       (uint_least32_t)
    435       mhd_str_dec_norm_uri_path (path_len,
    436                                  buff + path_start);
    437 
    438     do
    439     {
    440       size_t name_start;
    441       size_t name_len;
    442       size_t value_start;
    443       size_t value_len;
    444 
    445       value_start = 0u;
    446       for (name_start = i; i < uri_end; ++i) /* Processing parameter */
    447       {
    448         if ('+' == buff[i])
    449           buff[i] = ' ';
    450         else if ('=' == buff[i])
    451         {
    452           /* Found start of the value */
    453           for (value_start = ++i; i < uri_end; ++i) /* Processing parameter value */
    454           {
    455             if ('+' == buff[i])
    456               buff[i] = ' ';
    457             else if ('&' == buff[i]) /* delimiter for the next parameter */
    458               break; /* Next parameter */
    459           }
    460           break; /* End of the current parameter */
    461         }
    462         else if ('&' == buff[i])
    463           break; /* End of the name of the parameter without a value */
    464       }
    465 
    466       /* PCT-decode, zero-terminate and store the found parameter */
    467 
    468       if (0u != value_start) /* Value cannot start at zero position */
    469       { /* Name with value */
    470         mhd_assert (name_start + 1u <= value_start);
    471         name_len = value_start - name_start - 1u;
    472         value_len = i - value_start;
    473       }
    474       else
    475       { /* Name without value */
    476         name_len = i - name_start;
    477         value_len = 0u;
    478       }
    479       name_len = mhd_str_pct_decode_lenient_n (buff + name_start,
    480                                                name_len,
    481                                                buff + name_start,
    482                                                name_len,
    483                                                NULL); // TODO: add support for broken encoding detection
    484       buff[name_start + name_len] = 0;
    485 
    486       if (0u != value_start)
    487       {
    488         value_len =
    489           mhd_str_pct_decode_lenient_n (buff + name_start + name_len + 1u,
    490                                         value_len,
    491                                         buff + value_start,
    492                                         value_len,
    493                                         NULL); // TODO: add support for broken encoding detection
    494         buff[value_start + value_len] = 0;
    495       }
    496 
    497       if (! mhd_h2_items_reserve_new_item (ib))
    498         break; // TODO: support reporting no space errors
    499 
    500       mhd_h2_items_add_new_item_reserved (ib,
    501                                           name_start,
    502                                           name_len,
    503                                           value_len,
    504                                           (0u != value_start)
    505                                           ? mhd_H2_RIK_URI_PARAM :
    506                                           mhd_H2_RIK_URI_PARAM_NV);
    507 
    508     } while (uri_end > ++i);
    509 
    510   }
    511 
    512   return true;
    513 }
    514 
    515 
    516 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    517 MHD_FN_PAR_INOUT_ (1) bool
    518 mhd_h2_req_cookie_parse (struct mhd_H2Stream *restrict s)
    519 {
    520   // TODO: handle cookie combining
    521   // TODO: Implement cookie parsing
    522   return true;
    523 }