libmicrohttpd2

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

h2_proc_in.c (10011B)


      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_proc_in.c
     41  * @brief  Implementation of HTTP/2 connection incoming data processing 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 "mhd_assert.h"
     51 #include "mhd_unreachable.h"
     52 
     53 #include "mhd_buffer.h"
     54 #include "mhd_connection.h"
     55 
     56 #include <string.h>
     57 
     58 #include "h2_frame_types.h"
     59 #include "h2_frame_codec.h"
     60 
     61 #include "h2_conn_streams.h"
     62 #include "h2_proc_settings.h"
     63 #include "h2_proc_out.h"
     64 #include "h2_proc_conn.h"
     65 
     66 #include "h2_proc_in.h"
     67 
     68 
     69 enum mhd_H2ProcInFrameResult
     70 {
     71   mhd_H2_PROC_IN_FRAME_CONTINUE = mhd_H2_STNGS_PROC_OK,
     72   mhd_H2_PROC_IN_FRAME_BLOCKED_BY_OUT = mhd_H2_STNGS_PROC_NO_OUT_BUFF,
     73   mhd_H2_PROC_IN_FRAME_CONN_BROKEN = mhd_H2_STNGS_PROC_STNGS_ERR
     74 };
     75 
     76 /**
     77  * Process incoming frame
     78  * @param c the connection to use
     79  * @param h2frame the frame information
     80  * @param payload the frame payload (excluding known extra headers)
     81  * @return 'true' if frame was successfully and completely processed,
     82  *         'false' if frame should be processed again later or if connection
     83  *         if in closing or broken state (and no incoming frames should be
     84  *         processed anymore)
     85  */
     86 static MHD_FN_PAR_NONNULL_ALL_ bool
     87 process_inc_frame (struct MHD_Connection *restrict c,
     88                    const union mhd_H2FrameUnion *h2frame,
     89                    struct mhd_Buffer *payload)
     90 {
     91   if (0u != c->h2.state.continuation_stream_id)
     92   {
     93     if ((mhd_H2_FRAME_IDS_CONTINUATION_ID != h2frame->selector.type)
     94         || (c->h2.state.continuation_stream_id !=
     95             h2frame->continuation.stream_id))
     96       return ! mhd_h2_conn_finish (c,
     97                                    mhd_H2_ERR_PROTOCOL_ERROR,
     98                                    false);
     99   }
    100   else
    101     mhd_assert (0u == c->h2.buff.unproc_hdrs_size);
    102 
    103   switch (h2frame->selector.type)
    104   {
    105   case mhd_H2_FRAME_IDS_DATA_ID:
    106     mhd_assert (0);
    107     return mhd_h2_conn_finish (c,
    108                                mhd_H2_ERR_PROTOCOL_ERROR,
    109                                false);
    110   case mhd_H2_FRAME_IDS_HEADERS_ID:
    111     return mhd_h2_conn_streamid_in_headers (c,
    112                                             h2frame->headers.stream_id,
    113                                             h2frame->headers.end_stream,
    114                                             h2frame->headers.end_headers,
    115                                             payload);
    116   case mhd_H2_FRAME_IDS_RST_STREAM_ID:
    117     return mhd_h2_conn_streamid_in_rst_stream (c,
    118                                                h2frame->rst_stream.stream_id,
    119                                                h2frame->rst_stream.error_code);
    120     return mhd_H2_PROC_IN_FRAME_CONTINUE;
    121   case mhd_H2_FRAME_IDS_SETTINGS_ID:
    122     return (mhd_H2_STNGS_PROC_OK == mhd_h2_proc_new_settings (c,
    123                                                               payload));
    124   case mhd_H2_FRAME_IDS_PUSH_PROMISE_ID:
    125     return ! mhd_h2_conn_finish (c,
    126                                  mhd_H2_ERR_PROTOCOL_ERROR,
    127                                  false);
    128   case mhd_H2_FRAME_IDS_PING_ID:
    129     return mhd_h2_q_ping (c,
    130                           true,
    131                           h2frame->ping.opaque_data);
    132   case mhd_H2_FRAME_IDS_GOAWAY_ID:
    133     return mhd_h2_conn_process_in_goaway (c,
    134                                           h2frame->goaway.last_stream_id,
    135                                           h2frame->goaway.error_code);
    136   case mhd_H2_FRAME_IDS_WINDOW_UPDATE_ID:
    137     if (0u != h2frame->window_update.stream_id)
    138     {
    139       return
    140         mhd_h2_conn_streamid_window_incr (
    141         c,
    142         h2frame->window_update.stream_id,
    143         h2frame->window_update.window_size_increment);
    144     }
    145     if ((h2frame->window_update.window_size_increment
    146          + (uint_least32_t) c->h2.state.send_window) > 0x7FFFFFFFu)
    147       return ! mhd_h2_conn_finish (c,
    148                                    mhd_H2_ERR_FLOW_CONTROL_ERROR,
    149                                    false);
    150     c->h2.state.send_window +=
    151       (int_least32_t) h2frame->window_update.window_size_increment;
    152     return true;
    153   case mhd_H2_FRAME_IDS_CONTINUATION_ID:
    154     return
    155       mhd_h2_conn_streamid_in_continuation (c,
    156                                             h2frame->continuation.stream_id,
    157                                             h2frame->continuation.end_headers,
    158                                             payload);
    159   case mhd_H2_FRAME_IDS_PRIORITY_ID:
    160   default:
    161     break; /* Ignored */
    162   }
    163   /* Ignored types of frame */
    164   return true;
    165 }
    166 
    167 
    168 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
    169 mhd_h2_conn_process_in_data (struct MHD_Connection *restrict c)
    170 {
    171   mhd_assert (mhd_HTTP_LAYER_CONNECTED == c->h_layer.state);
    172 
    173   mhd_assert (c->read_buffer_size >= c->read_buffer_offset);
    174   mhd_assert (c->read_buffer_offset >= c->h2.buff.r_cur_frame);
    175 
    176   if (c->h2.buff.r_cur_frame == c->read_buffer_offset)
    177     return true; /* Shortcut, nothing to process */
    178 
    179   do
    180   {
    181     union mhd_H2FrameUnion h2frame;
    182     enum mhd_H2FrameDecodeResult dec_res;
    183     struct mhd_Buffer payload;
    184     bool proc_next;
    185     uint8_t *const frame_buff =
    186       (uint8_t *) c->read_buffer + c->h2.buff.r_cur_frame;
    187     const size_t buff_left = c->read_buffer_offset - c->h2.buff.r_cur_frame;
    188 
    189     proc_next = true;
    190     dec_res = mhd_h2_frame_decode (buff_left,
    191                                    frame_buff,
    192                                    c->h2.rcv_cfg.max_frame_size,
    193                                    &h2frame,
    194                                    &payload);
    195     switch (dec_res)
    196     {
    197     case mhd_H2_F_DEC_OK:
    198       proc_next = process_inc_frame (c,
    199                                      &h2frame,
    200                                      &payload);
    201       proc_next = (proc_next && (mhd_HTTP_LAYER_CONNECTED == c->h_layer.state));
    202       if (c->h2.state.top_seen_stream_id < h2frame.selector.stream_id)
    203         c->h2.state.top_seen_stream_id = h2frame.selector.stream_id;
    204       break;
    205     case mhd_H2_F_DEC_F_PAYLOAD_INCOMPLETE:
    206     case mhd_H2_F_DEC_F_HEADER_INCOMPLETE:
    207       proc_next = false;
    208       break;
    209     case mhd_H2_F_DEC_STREAM_ERR_F_SIZE:
    210     case mhd_H2_F_DEC_STREAM_ERR_PROT:
    211       if (mhd_h2_frame_get_total_size (&h2frame) > buff_left)
    212       {
    213         /* The frame is not yet complete */
    214         proc_next = false;
    215         break;
    216       }
    217       proc_next =
    218         mhd_h2_conn_streamid_abort (
    219           c,
    220           h2frame.selector.stream_id,
    221           (mhd_H2_F_DEC_STREAM_ERR_F_SIZE == dec_res) ?
    222           mhd_H2_ERR_FRAME_SIZE_ERROR : mhd_H2_ERR_PROTOCOL_ERROR);
    223       if (c->h2.state.top_seen_stream_id < h2frame.selector.stream_id)
    224         c->h2.state.top_seen_stream_id = h2frame.selector.stream_id;
    225       break;
    226     case mhd_H2_F_DEC_CONN_ERR_F_SIZE:
    227       mhd_h2_conn_finish (c,
    228                           mhd_H2_ERR_FRAME_SIZE_ERROR,
    229                           false);
    230       return false;
    231     case mhd_H2_F_DEC_CONN_ERR_PROT:
    232       mhd_h2_conn_finish (c,
    233                           mhd_H2_ERR_PROTOCOL_ERROR,
    234                           false);
    235       return false;
    236       break;
    237     default:
    238       break; /* ignore unknown types */
    239     }
    240 
    241     if (! proc_next)
    242       break;
    243 
    244     mhd_assert (mhd_HTTP_LAYER_CONNECTED == c->h_layer.state);
    245 
    246     mhd_assert ((c->read_buffer_offset - c->h2.buff.r_cur_frame) >=
    247                 mhd_h2_frame_get_total_size (&h2frame));
    248 
    249     c->h2.buff.r_cur_frame += mhd_h2_frame_get_total_size (&h2frame);
    250   } while (c->h2.buff.r_cur_frame < c->read_buffer_offset);
    251 
    252   mhd_assert (c->read_buffer_offset >= c->h2.buff.r_cur_frame);
    253   if (mhd_HTTP_LAYER_CONNECTED == c->h_layer.state)
    254   {
    255     const size_t data_left = c->read_buffer_offset - c->h2.buff.r_cur_frame;
    256 
    257     mhd_assert (data_left <= c->read_buffer_offset);
    258 
    259     memmove (c->read_buffer,
    260              c->read_buffer + c->h2.buff.unproc_hdrs_pos,
    261              c->h2.buff.unproc_hdrs_size);
    262     c->h2.buff.unproc_hdrs_pos = 0u;
    263 
    264     memmove (c->read_buffer + c->h2.buff.unproc_hdrs_size,
    265              c->read_buffer + c->h2.buff.r_cur_frame,
    266              data_left);
    267 
    268     c->h2.buff.r_cur_frame = c->h2.buff.unproc_hdrs_size;
    269     c->read_buffer_offset = c->h2.buff.r_cur_frame + data_left;
    270   }
    271 
    272   return (mhd_HTTP_LAYER_CONNECTED == c->h_layer.state);
    273 }