From 7f19a62dc12212c63e98acc090a1eca57b60c980 Mon Sep 17 00:00:00 2001 From: jikai Date: Tue, 19 Mar 2024 10:04:39 +0800 Subject: [PATCH] sync from upstream Signed-off-by: jikai (cherry picked from commit 7497c2f883d3e5238af5c18bb4273eb2e70ea2f6) --- ...roup-field-in-isulad-and-shim-config.patch | 42 + ...http-parser-as-third-party-component.patch | 3122 +++++++++++++++++ ...ields-in-shim-stats-and-container-in.patch | 53 + lcr.spec | 12 +- 4 files changed, 3228 insertions(+), 1 deletion(-) create mode 100644 0001-add-systemd-cgroup-field-in-isulad-and-shim-config.patch create mode 100644 0002-Add-http-parser-as-third-party-component.patch create mode 100644 0003-add-swap-usage-fields-in-shim-stats-and-container-in.patch diff --git a/0001-add-systemd-cgroup-field-in-isulad-and-shim-config.patch b/0001-add-systemd-cgroup-field-in-isulad-and-shim-config.patch new file mode 100644 index 0000000..048dd9c --- /dev/null +++ b/0001-add-systemd-cgroup-field-in-isulad-and-shim-config.patch @@ -0,0 +1,42 @@ +From 3bcb8f3f60abb6ab6cde999cadb6a836744d4104 Mon Sep 17 00:00:00 2001 +From: jikai +Date: Thu, 11 Jan 2024 19:00:19 +0800 +Subject: [PATCH 1/3] add systemd-cgroup field in isulad and shim config + +Signed-off-by: jikai +--- + src/json/schema/isulad-daemon-configs.json | 3 +++ + src/json/schema/shim/client/process-state.json | 3 +++ + 2 files changed, 6 insertions(+) + +diff --git a/src/json/schema/isulad-daemon-configs.json b/src/json/schema/isulad-daemon-configs.json +index 7a8657c..46cf9e2 100644 +--- a/src/json/schema/isulad-daemon-configs.json ++++ b/src/json/schema/isulad-daemon-configs.json +@@ -173,6 +173,9 @@ + }, + "enable-cri-v1": { + "type": "boolean" ++ }, ++ "systemd-cgroup": { ++ "type": "boolean" + } + } + } +diff --git a/src/json/schema/shim/client/process-state.json b/src/json/schema/shim/client/process-state.json +index 2383b43..15dbe91 100644 +--- a/src/json/schema/shim/client/process-state.json ++++ b/src/json/schema/shim/client/process-state.json +@@ -204,6 +204,9 @@ + }, + "logMaxsize": { + "type": "int64" ++ }, ++ "systemdCgroup": { ++ "type": "boolean" + } + } + } +-- +2.33.0 + diff --git a/0002-Add-http-parser-as-third-party-component.patch b/0002-Add-http-parser-as-third-party-component.patch new file mode 100644 index 0000000..db2f774 --- /dev/null +++ b/0002-Add-http-parser-as-third-party-component.patch @@ -0,0 +1,3122 @@ +From 30558b07c80895e748e4754010d3bfa99f1a52da Mon Sep 17 00:00:00 2001 +From: xuxuepeng +Date: Mon, 19 Feb 2024 23:41:50 +0800 +Subject: [PATCH 2/3] Add http-parser as third party component + +Signed-off-by: xuxuepeng +--- + CMakeLists.txt | 1 + + src/third_party/CMakeLists.txt | 3 + + src/third_party/http_parser/CMakeLists.txt | 35 + + src/third_party/http_parser/http_parser.c | 2567 ++++++++++++++++++++ + src/third_party/http_parser/http_parser.h | 443 ++++ + 5 files changed, 3049 insertions(+) + create mode 100644 src/third_party/http_parser/CMakeLists.txt + create mode 100644 src/third_party/http_parser/http_parser.c + create mode 100644 src/third_party/http_parser/http_parser.h + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index f668a27..300666d 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -120,6 +120,7 @@ install(FILES ${CMAKE_BINARY_DIR}/conf/libisula.pc + install(FILES src/runtime/lcrcontainer.h DESTINATION include/lcr) + install(FILES src/utils/utils_compile.h DESTINATION include/lcr) + ++install(FILES src/third_party/http_parser/http_parser.h DESTINATION include/isula_libutils) + install(FILES src/third_party/log.h DESTINATION include/isula_libutils) + install(FILES src/third_party/go_crc64.h DESTINATION include/isula_libutils) + install(FILES src/json/oci_runtime_hooks.h DESTINATION include/isula_libutils) +diff --git a/src/third_party/CMakeLists.txt b/src/third_party/CMakeLists.txt +index 06d8f47..f093ec5 100644 +--- a/src/third_party/CMakeLists.txt ++++ b/src/third_party/CMakeLists.txt +@@ -21,6 +21,7 @@ + # + + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/libocispec) ++add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/http_parser) + + # get current directory sources files + aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} local_utils_srcs) +@@ -28,12 +29,14 @@ aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} local_utils_srcs) + set(THIRD_PARTY_SRCS + ${local_utils_srcs} + ${LIBOCISPEC_SRCS} ++ ${HTTP_PARSER_SRCS} + PARENT_SCOPE + ) + + set(THIRD_PARTY_INCS + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/libocispec/ ++ ${HTTP_PARSER_INCS} + PARENT_SCOPE + ) + +diff --git a/src/third_party/http_parser/CMakeLists.txt b/src/third_party/http_parser/CMakeLists.txt +new file mode 100644 +index 0000000..be9a9dc +--- /dev/null ++++ b/src/third_party/http_parser/CMakeLists.txt +@@ -0,0 +1,35 @@ ++# iSula-libutils: import http_parser for iSula utils library ++# ++# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. ++# ++# Authors: ++# xuxuepeng ++# ++# This library is free software; you can redistribute it and/or ++# modify it under the terms of the GNU Lesser General Public ++# License as published by the Free Software Foundation; either ++# version 2.1 of the License, or (at your option) any later version. ++# ++# This library is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# Lesser General Public License for more details. ++# ++# You should have received a copy of the GNU Lesser General Public ++# License along with this library; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++# ++ ++# get current directory sources files ++aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} local_http_parser_srcs) ++ ++set(HTTP_PARSER_SRCS ++ ${local_http_parser_srcs} ++ PARENT_SCOPE ++ ) ++ ++set(HTTP_PARSER_INCS ++ ${CMAKE_CURRENT_SOURCE_DIR} ++ PARENT_SCOPE ++ ) ++ +diff --git a/src/third_party/http_parser/http_parser.c b/src/third_party/http_parser/http_parser.c +new file mode 100644 +index 0000000..2d8c08d +--- /dev/null ++++ b/src/third_party/http_parser/http_parser.c +@@ -0,0 +1,2567 @@ ++/* Copyright Joyent, Inc. and other Node contributors. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++#include "http_parser.h" ++#include ++#include ++#include ++#include ++#include ++ ++static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE; ++ ++#ifndef ULLONG_MAX ++# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ ++#endif ++ ++#ifndef MIN ++# define MIN(a,b) ((a) < (b) ? (a) : (b)) ++#endif ++ ++#ifndef ARRAY_SIZE ++# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) ++#endif ++ ++#ifndef BIT_AT ++# define BIT_AT(a, i) \ ++ (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ ++ (1 << ((unsigned int) (i) & 7)))) ++#endif ++ ++#ifndef ELEM_AT ++# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) ++#endif ++ ++#define SET_ERRNO(e) \ ++do { \ ++ parser->nread = nread; \ ++ parser->http_errno = (e); \ ++} while(0) ++ ++#define CURRENT_STATE() p_state ++#define UPDATE_STATE(V) p_state = (enum state) (V); ++#define RETURN(V) \ ++do { \ ++ parser->nread = nread; \ ++ parser->state = CURRENT_STATE(); \ ++ return (V); \ ++} while (0); ++#define REEXECUTE() \ ++ goto reexecute; \ ++ ++ ++#ifdef __GNUC__ ++# define LIKELY(X) __builtin_expect(!!(X), 1) ++# define UNLIKELY(X) __builtin_expect(!!(X), 0) ++#else ++# define LIKELY(X) (X) ++# define UNLIKELY(X) (X) ++#endif ++ ++ ++/* Run the notify callback FOR, returning ER if it fails */ ++#define CALLBACK_NOTIFY_(FOR, ER) \ ++do { \ ++ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ ++ \ ++ if (LIKELY(settings->on_##FOR)) { \ ++ parser->state = CURRENT_STATE(); \ ++ if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ ++ SET_ERRNO(HPE_CB_##FOR); \ ++ } \ ++ UPDATE_STATE(parser->state); \ ++ \ ++ /* We either errored above or got paused; get out */ \ ++ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ ++ return (ER); \ ++ } \ ++ } \ ++} while (0) ++ ++/* Run the notify callback FOR and consume the current byte */ ++#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) ++ ++/* Run the notify callback FOR and don't consume the current byte */ ++#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) ++ ++/* Run data callback FOR with LEN bytes, returning ER if it fails */ ++#define CALLBACK_DATA_(FOR, LEN, ER) \ ++do { \ ++ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ ++ \ ++ if (FOR##_mark) { \ ++ if (LIKELY(settings->on_##FOR)) { \ ++ parser->state = CURRENT_STATE(); \ ++ if (UNLIKELY(0 != \ ++ settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ ++ SET_ERRNO(HPE_CB_##FOR); \ ++ } \ ++ UPDATE_STATE(parser->state); \ ++ \ ++ /* We either errored above or got paused; get out */ \ ++ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ ++ return (ER); \ ++ } \ ++ } \ ++ FOR##_mark = NULL; \ ++ } \ ++} while (0) ++ ++/* Run the data callback FOR and consume the current byte */ ++#define CALLBACK_DATA(FOR) \ ++ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) ++ ++/* Run the data callback FOR and don't consume the current byte */ ++#define CALLBACK_DATA_NOADVANCE(FOR) \ ++ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) ++ ++/* Set the mark FOR; non-destructive if mark is already set */ ++#define MARK(FOR) \ ++do { \ ++ if (!FOR##_mark) { \ ++ FOR##_mark = p; \ ++ } \ ++} while (0) ++ ++/* Don't allow the total size of the HTTP headers (including the status ++ * line) to exceed max_header_size. This check is here to protect ++ * embedders against denial-of-service attacks where the attacker feeds ++ * us a never-ending header that the embedder keeps buffering. ++ * ++ * This check is arguably the responsibility of embedders but we're doing ++ * it on the embedder's behalf because most won't bother and this way we ++ * make the web a little safer. max_header_size is still far bigger ++ * than any reasonable request or response so this should never affect ++ * day-to-day operation. ++ */ ++#define COUNT_HEADER_SIZE(V) \ ++do { \ ++ nread += (uint32_t)(V); \ ++ if (UNLIKELY(nread > max_header_size)) { \ ++ SET_ERRNO(HPE_HEADER_OVERFLOW); \ ++ goto error; \ ++ } \ ++} while (0) ++ ++ ++#define PROXY_CONNECTION "proxy-connection" ++#define CONNECTION "connection" ++#define CONTENT_LENGTH "content-length" ++#define TRANSFER_ENCODING "transfer-encoding" ++#define UPGRADE "upgrade" ++#define CHUNKED "chunked" ++#define KEEP_ALIVE "keep-alive" ++#define CLOSE "close" ++ ++ ++static const char *method_strings[] = ++ { ++#define XX(num, name, string) #string, ++ HTTP_METHOD_MAP(XX) ++#undef XX ++ }; ++ ++ ++/* Tokens as defined by rfc 2616. Also lowercases them. ++ * token = 1* ++ * separators = "(" | ")" | "<" | ">" | "@" ++ * | "," | ";" | ":" | "\" | <"> ++ * | "/" | "[" | "]" | "?" | "=" ++ * | "{" | "}" | SP | HT ++ */ ++static const char tokens[256] = { ++/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ ++ 0, 0, 0, 0, 0, 0, 0, 0, ++/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ ++ 0, 0, 0, 0, 0, 0, 0, 0, ++/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ ++ 0, 0, 0, 0, 0, 0, 0, 0, ++/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ ++ 0, 0, 0, 0, 0, 0, 0, 0, ++/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ ++ ' ', '!', 0, '#', '$', '%', '&', '\'', ++/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ ++ 0, 0, '*', '+', 0, '-', '.', 0, ++/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ ++ '0', '1', '2', '3', '4', '5', '6', '7', ++/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ ++ '8', '9', 0, 0, 0, 0, 0, 0, ++/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ ++ 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', ++/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ ++ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', ++/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ ++ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', ++/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ ++ 'x', 'y', 'z', 0, 0, 0, '^', '_', ++/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ ++ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', ++/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ ++ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', ++/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ ++ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', ++/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ ++ 'x', 'y', 'z', 0, '|', 0, '~', 0 }; ++ ++ ++static const int8_t unhex[256] = ++ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++ , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 ++ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++ }; ++ ++ ++#if HTTP_PARSER_STRICT ++# define T(v) 0 ++#else ++# define T(v) v ++#endif ++ ++ ++static const uint8_t normal_url_char[32] = { ++/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ ++ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, ++/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ ++ 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, ++/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ ++ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, ++/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ ++ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, ++/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ ++ 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, ++/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ ++ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, ++/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ ++ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, ++/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ ++ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, ++/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ ++ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, ++/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ ++ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, ++/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ ++ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, ++/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ ++ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, ++/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ ++ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, ++/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ ++ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, ++/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ ++ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, ++/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ ++ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; ++ ++#undef T ++ ++enum state ++ { s_dead = 1 /* important that this is > 0 */ ++ ++ , s_start_req_or_res ++ , s_res_or_resp_H ++ , s_start_res ++ , s_res_H ++ , s_res_HT ++ , s_res_HTT ++ , s_res_HTTP ++ , s_res_http_major ++ , s_res_http_dot ++ , s_res_http_minor ++ , s_res_http_end ++ , s_res_first_status_code ++ , s_res_status_code ++ , s_res_status_start ++ , s_res_status ++ , s_res_line_almost_done ++ ++ , s_start_req ++ ++ , s_req_method ++ , s_req_spaces_before_url ++ , s_req_schema ++ , s_req_schema_slash ++ , s_req_schema_slash_slash ++ , s_req_server_start ++ , s_req_server ++ , s_req_server_with_at ++ , s_req_path ++ , s_req_query_string_start ++ , s_req_query_string ++ , s_req_fragment_start ++ , s_req_fragment ++ , s_req_http_start ++ , s_req_http_H ++ , s_req_http_HT ++ , s_req_http_HTT ++ , s_req_http_HTTP ++ , s_req_http_I ++ , s_req_http_IC ++ , s_req_http_major ++ , s_req_http_dot ++ , s_req_http_minor ++ , s_req_http_end ++ , s_req_line_almost_done ++ ++ , s_header_field_start ++ , s_header_field ++ , s_header_value_discard_ws ++ , s_header_value_discard_ws_almost_done ++ , s_header_value_discard_lws ++ , s_header_value_start ++ , s_header_value ++ , s_header_value_lws ++ ++ , s_header_almost_done ++ ++ , s_chunk_size_start ++ , s_chunk_size ++ , s_chunk_parameters ++ , s_chunk_size_almost_done ++ ++ , s_headers_almost_done ++ , s_headers_done ++ ++ /* Important: 's_headers_done' must be the last 'header' state. All ++ * states beyond this must be 'body' states. It is used for overflow ++ * checking. See the PARSING_HEADER() macro. ++ */ ++ ++ , s_chunk_data ++ , s_chunk_data_almost_done ++ , s_chunk_data_done ++ ++ , s_body_identity ++ , s_body_identity_eof ++ ++ , s_message_done ++ }; ++ ++ ++#define PARSING_HEADER(state) (state <= s_headers_done) ++ ++ ++enum header_states ++ { h_general = 0 ++ , h_C ++ , h_CO ++ , h_CON ++ ++ , h_matching_connection ++ , h_matching_proxy_connection ++ , h_matching_content_length ++ , h_matching_transfer_encoding ++ , h_matching_upgrade ++ ++ , h_connection ++ , h_content_length ++ , h_content_length_num ++ , h_content_length_ws ++ , h_transfer_encoding ++ , h_upgrade ++ ++ , h_matching_transfer_encoding_token_start ++ , h_matching_transfer_encoding_chunked ++ , h_matching_transfer_encoding_token ++ ++ , h_matching_connection_token_start ++ , h_matching_connection_keep_alive ++ , h_matching_connection_close ++ , h_matching_connection_upgrade ++ , h_matching_connection_token ++ ++ , h_transfer_encoding_chunked ++ , h_connection_keep_alive ++ , h_connection_close ++ , h_connection_upgrade ++ }; ++ ++enum http_host_state ++ { ++ s_http_host_dead = 1 ++ , s_http_userinfo_start ++ , s_http_userinfo ++ , s_http_host_start ++ , s_http_host_v6_start ++ , s_http_host ++ , s_http_host_v6 ++ , s_http_host_v6_end ++ , s_http_host_v6_zone_start ++ , s_http_host_v6_zone ++ , s_http_host_port_start ++ , s_http_host_port ++}; ++ ++/* Macros for character classes; depends on strict-mode */ ++#define CR '\r' ++#define LF '\n' ++#define LOWER(c) (unsigned char)(c | 0x20) ++#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') ++#define IS_NUM(c) ((c) >= '0' && (c) <= '9') ++#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) ++#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) ++#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ ++ (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ ++ (c) == ')') ++#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ ++ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ ++ (c) == '$' || (c) == ',') ++ ++#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c]) ++ ++#if HTTP_PARSER_STRICT ++#define TOKEN(c) STRICT_TOKEN(c) ++#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) ++#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') ++#else ++#define TOKEN(c) tokens[(unsigned char)c] ++#define IS_URL_CHAR(c) \ ++ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) ++#define IS_HOST_CHAR(c) \ ++ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') ++#endif ++ ++/** ++ * Verify that a char is a valid visible (printable) US-ASCII ++ * character or %x80-FF ++ **/ ++#define IS_HEADER_CHAR(ch) \ ++ (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127)) ++ ++#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) ++ ++ ++#if HTTP_PARSER_STRICT ++# define STRICT_CHECK(cond) \ ++do { \ ++ if (cond) { \ ++ SET_ERRNO(HPE_STRICT); \ ++ goto error; \ ++ } \ ++} while (0) ++# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) ++#else ++# define STRICT_CHECK(cond) ++# define NEW_MESSAGE() start_state ++#endif ++ ++ ++/* Map errno values to strings for human-readable output */ ++#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, ++static struct { ++ const char *name; ++ const char *description; ++} http_strerror_tab[] = { ++ HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) ++}; ++#undef HTTP_STRERROR_GEN ++ ++int http_message_needs_eof(const http_parser *parser); ++ ++/* Our URL parser. ++ * ++ * This is designed to be shared by http_parser_execute() for URL validation, ++ * hence it has a state transition + byte-for-byte interface. In addition, it ++ * is meant to be embedded in http_parser_parse_url(), which does the dirty ++ * work of turning state transitions URL components for its API. ++ * ++ * This function should only be invoked with non-space characters. It is ++ * assumed that the caller cares about (and can detect) the transition between ++ * URL and non-URL states by looking for these. ++ */ ++static enum state ++parse_url_char(enum state s, const char ch) ++{ ++ if (ch == ' ' || ch == '\r' || ch == '\n') { ++ return s_dead; ++ } ++ ++#if HTTP_PARSER_STRICT ++ if (ch == '\t' || ch == '\f') { ++ return s_dead; ++ } ++#endif ++ ++ switch (s) { ++ case s_req_spaces_before_url: ++ /* Proxied requests are followed by scheme of an absolute URI (alpha). ++ * All methods except CONNECT are followed by '/' or '*'. ++ */ ++ ++ if (ch == '/' || ch == '*') { ++ return s_req_path; ++ } ++ ++ if (IS_ALPHA(ch)) { ++ return s_req_schema; ++ } ++ ++ break; ++ ++ case s_req_schema: ++ if (IS_ALPHA(ch)) { ++ return s; ++ } ++ ++ if (ch == ':') { ++ return s_req_schema_slash; ++ } ++ ++ break; ++ ++ case s_req_schema_slash: ++ if (ch == '/') { ++ return s_req_schema_slash_slash; ++ } ++ ++ break; ++ ++ case s_req_schema_slash_slash: ++ if (ch == '/') { ++ return s_req_server_start; ++ } ++ ++ break; ++ ++ case s_req_server_with_at: ++ if (ch == '@') { ++ return s_dead; ++ } ++ ++ /* fall through */ ++ case s_req_server_start: ++ case s_req_server: ++ if (ch == '/') { ++ return s_req_path; ++ } ++ ++ if (ch == '?') { ++ return s_req_query_string_start; ++ } ++ ++ if (ch == '@') { ++ return s_req_server_with_at; ++ } ++ ++ if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { ++ return s_req_server; ++ } ++ ++ break; ++ ++ case s_req_path: ++ if (IS_URL_CHAR(ch)) { ++ return s; ++ } ++ ++ switch (ch) { ++ case '?': ++ return s_req_query_string_start; ++ ++ case '#': ++ return s_req_fragment_start; ++ } ++ ++ break; ++ ++ case s_req_query_string_start: ++ case s_req_query_string: ++ if (IS_URL_CHAR(ch)) { ++ return s_req_query_string; ++ } ++ ++ switch (ch) { ++ case '?': ++ /* allow extra '?' in query string */ ++ return s_req_query_string; ++ ++ case '#': ++ return s_req_fragment_start; ++ } ++ ++ break; ++ ++ case s_req_fragment_start: ++ if (IS_URL_CHAR(ch)) { ++ return s_req_fragment; ++ } ++ ++ switch (ch) { ++ case '?': ++ return s_req_fragment; ++ ++ case '#': ++ return s; ++ } ++ ++ break; ++ ++ case s_req_fragment: ++ if (IS_URL_CHAR(ch)) { ++ return s; ++ } ++ ++ switch (ch) { ++ case '?': ++ case '#': ++ return s; ++ } ++ ++ break; ++ ++ default: ++ break; ++ } ++ ++ /* We should never fall out of the switch above unless there's an error */ ++ return s_dead; ++} ++ ++size_t http_parser_execute (http_parser *parser, ++ const http_parser_settings *settings, ++ const char *data, ++ size_t len) ++{ ++ char c, ch; ++ int8_t unhex_val; ++ const char *p = data; ++ const char *header_field_mark = 0; ++ const char *header_value_mark = 0; ++ const char *url_mark = 0; ++ const char *body_mark = 0; ++ const char *status_mark = 0; ++ enum state p_state = (enum state) parser->state; ++ const unsigned int lenient = parser->lenient_http_headers; ++ uint32_t nread = parser->nread; ++ ++ /* We're in an error state. Don't bother doing anything. */ ++ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { ++ return 0; ++ } ++ ++ if (len == 0) { ++ switch (CURRENT_STATE()) { ++ case s_body_identity_eof: ++ /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if ++ * we got paused. ++ */ ++ CALLBACK_NOTIFY_NOADVANCE(message_complete); ++ return 0; ++ ++ case s_dead: ++ case s_start_req_or_res: ++ case s_start_res: ++ case s_start_req: ++ return 0; ++ ++ default: ++ SET_ERRNO(HPE_INVALID_EOF_STATE); ++ return 1; ++ } ++ } ++ ++ ++ if (CURRENT_STATE() == s_header_field) ++ header_field_mark = data; ++ if (CURRENT_STATE() == s_header_value) ++ header_value_mark = data; ++ switch (CURRENT_STATE()) { ++ case s_req_path: ++ case s_req_schema: ++ case s_req_schema_slash: ++ case s_req_schema_slash_slash: ++ case s_req_server_start: ++ case s_req_server: ++ case s_req_server_with_at: ++ case s_req_query_string_start: ++ case s_req_query_string: ++ case s_req_fragment_start: ++ case s_req_fragment: ++ url_mark = data; ++ break; ++ case s_res_status: ++ status_mark = data; ++ break; ++ default: ++ break; ++ } ++ ++ for (p=data; p != data + len; p++) { ++ ch = *p; ++ ++ if (PARSING_HEADER(CURRENT_STATE())) ++ COUNT_HEADER_SIZE(1); ++ ++reexecute: ++ switch (CURRENT_STATE()) { ++ ++ case s_dead: ++ /* this state is used after a 'Connection: close' message ++ * the parser will error out if it reads another message ++ */ ++ if (LIKELY(ch == CR || ch == LF)) ++ break; ++ ++ SET_ERRNO(HPE_CLOSED_CONNECTION); ++ goto error; ++ ++ case s_start_req_or_res: ++ { ++ if (ch == CR || ch == LF) ++ break; ++ parser->flags = 0; ++ parser->extra_flags = 0; ++ parser->content_length = ULLONG_MAX; ++ ++ if (ch == 'H') { ++ UPDATE_STATE(s_res_or_resp_H); ++ ++ CALLBACK_NOTIFY(message_begin); ++ } else { ++ parser->type = HTTP_REQUEST; ++ UPDATE_STATE(s_start_req); ++ REEXECUTE(); ++ } ++ ++ break; ++ } ++ ++ case s_res_or_resp_H: ++ if (ch == 'T') { ++ parser->type = HTTP_RESPONSE; ++ UPDATE_STATE(s_res_HT); ++ } else { ++ if (UNLIKELY(ch != 'E')) { ++ SET_ERRNO(HPE_INVALID_CONSTANT); ++ goto error; ++ } ++ ++ parser->type = HTTP_REQUEST; ++ parser->method = HTTP_HEAD; ++ parser->index = 2; ++ UPDATE_STATE(s_req_method); ++ } ++ break; ++ ++ case s_start_res: ++ { ++ if (ch == CR || ch == LF) ++ break; ++ parser->flags = 0; ++ parser->extra_flags = 0; ++ parser->content_length = ULLONG_MAX; ++ ++ if (ch == 'H') { ++ UPDATE_STATE(s_res_H); ++ } else { ++ SET_ERRNO(HPE_INVALID_CONSTANT); ++ goto error; ++ } ++ ++ CALLBACK_NOTIFY(message_begin); ++ break; ++ } ++ ++ case s_res_H: ++ STRICT_CHECK(ch != 'T'); ++ UPDATE_STATE(s_res_HT); ++ break; ++ ++ case s_res_HT: ++ STRICT_CHECK(ch != 'T'); ++ UPDATE_STATE(s_res_HTT); ++ break; ++ ++ case s_res_HTT: ++ STRICT_CHECK(ch != 'P'); ++ UPDATE_STATE(s_res_HTTP); ++ break; ++ ++ case s_res_HTTP: ++ STRICT_CHECK(ch != '/'); ++ UPDATE_STATE(s_res_http_major); ++ break; ++ ++ case s_res_http_major: ++ if (UNLIKELY(!IS_NUM(ch))) { ++ SET_ERRNO(HPE_INVALID_VERSION); ++ goto error; ++ } ++ ++ parser->http_major = ch - '0'; ++ UPDATE_STATE(s_res_http_dot); ++ break; ++ ++ case s_res_http_dot: ++ { ++ if (UNLIKELY(ch != '.')) { ++ SET_ERRNO(HPE_INVALID_VERSION); ++ goto error; ++ } ++ ++ UPDATE_STATE(s_res_http_minor); ++ break; ++ } ++ ++ case s_res_http_minor: ++ if (UNLIKELY(!IS_NUM(ch))) { ++ SET_ERRNO(HPE_INVALID_VERSION); ++ goto error; ++ } ++ ++ parser->http_minor = ch - '0'; ++ UPDATE_STATE(s_res_http_end); ++ break; ++ ++ case s_res_http_end: ++ { ++ if (UNLIKELY(ch != ' ')) { ++ SET_ERRNO(HPE_INVALID_VERSION); ++ goto error; ++ } ++ ++ UPDATE_STATE(s_res_first_status_code); ++ break; ++ } ++ ++ case s_res_first_status_code: ++ { ++ if (!IS_NUM(ch)) { ++ if (ch == ' ') { ++ break; ++ } ++ ++ SET_ERRNO(HPE_INVALID_STATUS); ++ goto error; ++ } ++ parser->status_code = ch - '0'; ++ UPDATE_STATE(s_res_status_code); ++ break; ++ } ++ ++ case s_res_status_code: ++ { ++ if (!IS_NUM(ch)) { ++ switch (ch) { ++ case ' ': ++ UPDATE_STATE(s_res_status_start); ++ break; ++ case CR: ++ case LF: ++ UPDATE_STATE(s_res_status_start); ++ REEXECUTE(); ++ break; ++ default: ++ SET_ERRNO(HPE_INVALID_STATUS); ++ goto error; ++ } ++ break; ++ } ++ ++ parser->status_code *= 10; ++ parser->status_code += ch - '0'; ++ ++ if (UNLIKELY(parser->status_code > 999)) { ++ SET_ERRNO(HPE_INVALID_STATUS); ++ goto error; ++ } ++ ++ break; ++ } ++ ++ case s_res_status_start: ++ { ++ MARK(status); ++ UPDATE_STATE(s_res_status); ++ parser->index = 0; ++ ++ if (ch == CR || ch == LF) ++ REEXECUTE(); ++ ++ break; ++ } ++ ++ case s_res_status: ++ if (ch == CR) { ++ UPDATE_STATE(s_res_line_almost_done); ++ CALLBACK_DATA(status); ++ break; ++ } ++ ++ if (ch == LF) { ++ UPDATE_STATE(s_header_field_start); ++ CALLBACK_DATA(status); ++ break; ++ } ++ ++ break; ++ ++ case s_res_line_almost_done: ++ STRICT_CHECK(ch != LF); ++ UPDATE_STATE(s_header_field_start); ++ break; ++ ++ case s_start_req: ++ { ++ if (ch == CR || ch == LF) ++ break; ++ parser->flags = 0; ++ parser->extra_flags = 0; ++ parser->content_length = ULLONG_MAX; ++ ++ if (UNLIKELY(!IS_ALPHA(ch))) { ++ SET_ERRNO(HPE_INVALID_METHOD); ++ goto error; ++ } ++ ++ parser->method = (enum http_method) 0; ++ parser->index = 1; ++ switch (ch) { ++ case 'A': parser->method = HTTP_ACL; break; ++ case 'B': parser->method = HTTP_BIND; break; ++ case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; ++ case 'D': parser->method = HTTP_DELETE; break; ++ case 'G': parser->method = HTTP_GET; break; ++ case 'H': parser->method = HTTP_HEAD; break; ++ case 'L': parser->method = HTTP_LOCK; /* or LINK */ break; ++ case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; ++ case 'N': parser->method = HTTP_NOTIFY; break; ++ case 'O': parser->method = HTTP_OPTIONS; break; ++ case 'P': parser->method = HTTP_POST; ++ /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ ++ break; ++ case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break; ++ case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break; ++ case 'T': parser->method = HTTP_TRACE; break; ++ case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break; ++ default: ++ SET_ERRNO(HPE_INVALID_METHOD); ++ goto error; ++ } ++ UPDATE_STATE(s_req_method); ++ ++ CALLBACK_NOTIFY(message_begin); ++ ++ break; ++ } ++ ++ case s_req_method: ++ { ++ const char *matcher; ++ if (UNLIKELY(ch == '\0')) { ++ SET_ERRNO(HPE_INVALID_METHOD); ++ goto error; ++ } ++ ++ matcher = method_strings[parser->method]; ++ if (ch == ' ' && matcher[parser->index] == '\0') { ++ UPDATE_STATE(s_req_spaces_before_url); ++ } else if (ch == matcher[parser->index]) { ++ ; /* nada */ ++ } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') { ++ ++ switch (parser->method << 16 | parser->index << 8 | ch) { ++#define XX(meth, pos, ch, new_meth) \ ++ case (HTTP_##meth << 16 | pos << 8 | ch): \ ++ parser->method = HTTP_##new_meth; break; ++ ++ XX(POST, 1, 'U', PUT) ++ XX(POST, 1, 'A', PATCH) ++ XX(POST, 1, 'R', PROPFIND) ++ XX(PUT, 2, 'R', PURGE) ++ XX(CONNECT, 1, 'H', CHECKOUT) ++ XX(CONNECT, 2, 'P', COPY) ++ XX(MKCOL, 1, 'O', MOVE) ++ XX(MKCOL, 1, 'E', MERGE) ++ XX(MKCOL, 1, '-', MSEARCH) ++ XX(MKCOL, 2, 'A', MKACTIVITY) ++ XX(MKCOL, 3, 'A', MKCALENDAR) ++ XX(SUBSCRIBE, 1, 'E', SEARCH) ++ XX(SUBSCRIBE, 1, 'O', SOURCE) ++ XX(REPORT, 2, 'B', REBIND) ++ XX(PROPFIND, 4, 'P', PROPPATCH) ++ XX(LOCK, 1, 'I', LINK) ++ XX(UNLOCK, 2, 'S', UNSUBSCRIBE) ++ XX(UNLOCK, 2, 'B', UNBIND) ++ XX(UNLOCK, 3, 'I', UNLINK) ++#undef XX ++ default: ++ SET_ERRNO(HPE_INVALID_METHOD); ++ goto error; ++ } ++ } else { ++ SET_ERRNO(HPE_INVALID_METHOD); ++ goto error; ++ } ++ ++ ++parser->index; ++ break; ++ } ++ ++ case s_req_spaces_before_url: ++ { ++ if (ch == ' ') break; ++ ++ MARK(url); ++ if (parser->method == HTTP_CONNECT) { ++ UPDATE_STATE(s_req_server_start); ++ } ++ ++ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); ++ if (UNLIKELY(CURRENT_STATE() == s_dead)) { ++ SET_ERRNO(HPE_INVALID_URL); ++ goto error; ++ } ++ ++ break; ++ } ++ ++ case s_req_schema: ++ case s_req_schema_slash: ++ case s_req_schema_slash_slash: ++ case s_req_server_start: ++ { ++ switch (ch) { ++ /* No whitespace allowed here */ ++ case ' ': ++ case CR: ++ case LF: ++ SET_ERRNO(HPE_INVALID_URL); ++ goto error; ++ default: ++ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); ++ if (UNLIKELY(CURRENT_STATE() == s_dead)) { ++ SET_ERRNO(HPE_INVALID_URL); ++ goto error; ++ } ++ } ++ ++ break; ++ } ++ ++ case s_req_server: ++ case s_req_server_with_at: ++ case s_req_path: ++ case s_req_query_string_start: ++ case s_req_query_string: ++ case s_req_fragment_start: ++ case s_req_fragment: ++ { ++ switch (ch) { ++ case ' ': ++ UPDATE_STATE(s_req_http_start); ++ CALLBACK_DATA(url); ++ break; ++ case CR: ++ case LF: ++ parser->http_major = 0; ++ parser->http_minor = 9; ++ UPDATE_STATE((ch == CR) ? ++ s_req_line_almost_done : ++ s_header_field_start); ++ CALLBACK_DATA(url); ++ break; ++ default: ++ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); ++ if (UNLIKELY(CURRENT_STATE() == s_dead)) { ++ SET_ERRNO(HPE_INVALID_URL); ++ goto error; ++ } ++ } ++ break; ++ } ++ ++ case s_req_http_start: ++ switch (ch) { ++ case ' ': ++ break; ++ case 'H': ++ UPDATE_STATE(s_req_http_H); ++ break; ++ case 'I': ++ if (parser->method == HTTP_SOURCE) { ++ UPDATE_STATE(s_req_http_I); ++ break; ++ } ++ /* fall through */ ++ default: ++ SET_ERRNO(HPE_INVALID_CONSTANT); ++ goto error; ++ } ++ break; ++ ++ case s_req_http_H: ++ STRICT_CHECK(ch != 'T'); ++ UPDATE_STATE(s_req_http_HT); ++ break; ++ ++ case s_req_http_HT: ++ STRICT_CHECK(ch != 'T'); ++ UPDATE_STATE(s_req_http_HTT); ++ break; ++ ++ case s_req_http_HTT: ++ STRICT_CHECK(ch != 'P'); ++ UPDATE_STATE(s_req_http_HTTP); ++ break; ++ ++ case s_req_http_I: ++ STRICT_CHECK(ch != 'C'); ++ UPDATE_STATE(s_req_http_IC); ++ break; ++ ++ case s_req_http_IC: ++ STRICT_CHECK(ch != 'E'); ++ UPDATE_STATE(s_req_http_HTTP); /* Treat "ICE" as "HTTP". */ ++ break; ++ ++ case s_req_http_HTTP: ++ STRICT_CHECK(ch != '/'); ++ UPDATE_STATE(s_req_http_major); ++ break; ++ ++ case s_req_http_major: ++ if (UNLIKELY(!IS_NUM(ch))) { ++ SET_ERRNO(HPE_INVALID_VERSION); ++ goto error; ++ } ++ ++ parser->http_major = ch - '0'; ++ UPDATE_STATE(s_req_http_dot); ++ break; ++ ++ case s_req_http_dot: ++ { ++ if (UNLIKELY(ch != '.')) { ++ SET_ERRNO(HPE_INVALID_VERSION); ++ goto error; ++ } ++ ++ UPDATE_STATE(s_req_http_minor); ++ break; ++ } ++ ++ case s_req_http_minor: ++ if (UNLIKELY(!IS_NUM(ch))) { ++ SET_ERRNO(HPE_INVALID_VERSION); ++ goto error; ++ } ++ ++ parser->http_minor = ch - '0'; ++ UPDATE_STATE(s_req_http_end); ++ break; ++ ++ case s_req_http_end: ++ { ++ if (ch == CR) { ++ UPDATE_STATE(s_req_line_almost_done); ++ break; ++ } ++ ++ if (ch == LF) { ++ UPDATE_STATE(s_header_field_start); ++ break; ++ } ++ ++ SET_ERRNO(HPE_INVALID_VERSION); ++ goto error; ++ break; ++ } ++ ++ /* end of request line */ ++ case s_req_line_almost_done: ++ { ++ if (UNLIKELY(ch != LF)) { ++ SET_ERRNO(HPE_LF_EXPECTED); ++ goto error; ++ } ++ ++ UPDATE_STATE(s_header_field_start); ++ break; ++ } ++ ++ case s_header_field_start: ++ { ++ if (ch == CR) { ++ UPDATE_STATE(s_headers_almost_done); ++ break; ++ } ++ ++ if (ch == LF) { ++ /* they might be just sending \n instead of \r\n so this would be ++ * the second \n to denote the end of headers*/ ++ UPDATE_STATE(s_headers_almost_done); ++ REEXECUTE(); ++ } ++ ++ c = TOKEN(ch); ++ ++ if (UNLIKELY(!c)) { ++ SET_ERRNO(HPE_INVALID_HEADER_TOKEN); ++ goto error; ++ } ++ ++ MARK(header_field); ++ ++ parser->index = 0; ++ UPDATE_STATE(s_header_field); ++ ++ switch (c) { ++ case 'c': ++ parser->header_state = h_C; ++ break; ++ ++ case 'p': ++ parser->header_state = h_matching_proxy_connection; ++ break; ++ ++ case 't': ++ parser->header_state = h_matching_transfer_encoding; ++ break; ++ ++ case 'u': ++ parser->header_state = h_matching_upgrade; ++ break; ++ ++ default: ++ parser->header_state = h_general; ++ break; ++ } ++ break; ++ } ++ ++ case s_header_field: ++ { ++ const char* start = p; ++ for (; p != data + len; p++) { ++ ch = *p; ++ c = TOKEN(ch); ++ ++ if (!c) ++ break; ++ ++ switch (parser->header_state) { ++ case h_general: { ++ size_t left = data + len - p; ++ const char* pe = p + MIN(left, max_header_size); ++ while (p+1 < pe && TOKEN(p[1])) { ++ p++; ++ } ++ break; ++ } ++ ++ case h_C: ++ parser->index++; ++ parser->header_state = (c == 'o' ? h_CO : h_general); ++ break; ++ ++ case h_CO: ++ parser->index++; ++ parser->header_state = (c == 'n' ? h_CON : h_general); ++ break; ++ ++ case h_CON: ++ parser->index++; ++ switch (c) { ++ case 'n': ++ parser->header_state = h_matching_connection; ++ break; ++ case 't': ++ parser->header_state = h_matching_content_length; ++ break; ++ default: ++ parser->header_state = h_general; ++ break; ++ } ++ break; ++ ++ /* connection */ ++ ++ case h_matching_connection: ++ parser->index++; ++ if (parser->index > sizeof(CONNECTION)-1 ++ || c != CONNECTION[parser->index]) { ++ parser->header_state = h_general; ++ } else if (parser->index == sizeof(CONNECTION)-2) { ++ parser->header_state = h_connection; ++ } ++ break; ++ ++ /* proxy-connection */ ++ ++ case h_matching_proxy_connection: ++ parser->index++; ++ if (parser->index > sizeof(PROXY_CONNECTION)-1 ++ || c != PROXY_CONNECTION[parser->index]) { ++ parser->header_state = h_general; ++ } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { ++ parser->header_state = h_connection; ++ } ++ break; ++ ++ /* content-length */ ++ ++ case h_matching_content_length: ++ parser->index++; ++ if (parser->index > sizeof(CONTENT_LENGTH)-1 ++ || c != CONTENT_LENGTH[parser->index]) { ++ parser->header_state = h_general; ++ } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { ++ parser->header_state = h_content_length; ++ } ++ break; ++ ++ /* transfer-encoding */ ++ ++ case h_matching_transfer_encoding: ++ parser->index++; ++ if (parser->index > sizeof(TRANSFER_ENCODING)-1 ++ || c != TRANSFER_ENCODING[parser->index]) { ++ parser->header_state = h_general; ++ } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { ++ parser->header_state = h_transfer_encoding; ++ parser->extra_flags |= F_TRANSFER_ENCODING >> 8; ++ } ++ break; ++ ++ /* upgrade */ ++ ++ case h_matching_upgrade: ++ parser->index++; ++ if (parser->index > sizeof(UPGRADE)-1 ++ || c != UPGRADE[parser->index]) { ++ parser->header_state = h_general; ++ } else if (parser->index == sizeof(UPGRADE)-2) { ++ parser->header_state = h_upgrade; ++ } ++ break; ++ ++ case h_connection: ++ case h_content_length: ++ case h_transfer_encoding: ++ case h_upgrade: ++ if (ch != ' ') parser->header_state = h_general; ++ break; ++ ++ default: ++ assert(0 && "Unknown header_state"); ++ break; ++ } ++ } ++ ++ if (p == data + len) { ++ --p; ++ COUNT_HEADER_SIZE(p - start); ++ break; ++ } ++ ++ COUNT_HEADER_SIZE(p - start); ++ ++ if (ch == ':') { ++ UPDATE_STATE(s_header_value_discard_ws); ++ CALLBACK_DATA(header_field); ++ break; ++ } ++ ++ SET_ERRNO(HPE_INVALID_HEADER_TOKEN); ++ goto error; ++ } ++ ++ case s_header_value_discard_ws: ++ if (ch == ' ' || ch == '\t') break; ++ ++ if (ch == CR) { ++ UPDATE_STATE(s_header_value_discard_ws_almost_done); ++ break; ++ } ++ ++ if (ch == LF) { ++ UPDATE_STATE(s_header_value_discard_lws); ++ break; ++ } ++ ++ /* fall through */ ++ ++ case s_header_value_start: ++ { ++ MARK(header_value); ++ ++ UPDATE_STATE(s_header_value); ++ parser->index = 0; ++ ++ c = LOWER(ch); ++ ++ switch (parser->header_state) { ++ case h_upgrade: ++ parser->flags |= F_UPGRADE; ++ parser->header_state = h_general; ++ break; ++ ++ case h_transfer_encoding: ++ /* looking for 'Transfer-Encoding: chunked' */ ++ if ('c' == c) { ++ parser->header_state = h_matching_transfer_encoding_chunked; ++ } else { ++ parser->header_state = h_matching_transfer_encoding_token; ++ } ++ break; ++ ++ /* Multi-value `Transfer-Encoding` header */ ++ case h_matching_transfer_encoding_token_start: ++ break; ++ ++ case h_content_length: ++ if (UNLIKELY(!IS_NUM(ch))) { ++ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); ++ goto error; ++ } ++ ++ if (parser->flags & F_CONTENTLENGTH) { ++ SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); ++ goto error; ++ } ++ ++ parser->flags |= F_CONTENTLENGTH; ++ parser->content_length = ch - '0'; ++ parser->header_state = h_content_length_num; ++ break; ++ ++ /* when obsolete line folding is encountered for content length ++ * continue to the s_header_value state */ ++ case h_content_length_ws: ++ break; ++ ++ case h_connection: ++ /* looking for 'Connection: keep-alive' */ ++ if (c == 'k') { ++ parser->header_state = h_matching_connection_keep_alive; ++ /* looking for 'Connection: close' */ ++ } else if (c == 'c') { ++ parser->header_state = h_matching_connection_close; ++ } else if (c == 'u') { ++ parser->header_state = h_matching_connection_upgrade; ++ } else { ++ parser->header_state = h_matching_connection_token; ++ } ++ break; ++ ++ /* Multi-value `Connection` header */ ++ case h_matching_connection_token_start: ++ break; ++ ++ default: ++ parser->header_state = h_general; ++ break; ++ } ++ break; ++ } ++ ++ case s_header_value: ++ { ++ const char* start = p; ++ enum header_states h_state = (enum header_states) parser->header_state; ++ for (; p != data + len; p++) { ++ ch = *p; ++ if (ch == CR) { ++ UPDATE_STATE(s_header_almost_done); ++ parser->header_state = h_state; ++ CALLBACK_DATA(header_value); ++ break; ++ } ++ ++ if (ch == LF) { ++ UPDATE_STATE(s_header_almost_done); ++ COUNT_HEADER_SIZE(p - start); ++ parser->header_state = h_state; ++ CALLBACK_DATA_NOADVANCE(header_value); ++ REEXECUTE(); ++ } ++ ++ if (!lenient && !IS_HEADER_CHAR(ch)) { ++ SET_ERRNO(HPE_INVALID_HEADER_TOKEN); ++ goto error; ++ } ++ ++ c = LOWER(ch); ++ ++ switch (h_state) { ++ case h_general: ++ { ++ size_t left = data + len - p; ++ const char* pe = p + MIN(left, max_header_size); ++ ++ for (; p != pe; p++) { ++ ch = *p; ++ if (ch == CR || ch == LF) { ++ --p; ++ break; ++ } ++ if (!lenient && !IS_HEADER_CHAR(ch)) { ++ SET_ERRNO(HPE_INVALID_HEADER_TOKEN); ++ goto error; ++ } ++ } ++ if (p == data + len) ++ --p; ++ break; ++ } ++ ++ case h_connection: ++ case h_transfer_encoding: ++ assert(0 && "Shouldn't get here."); ++ break; ++ ++ case h_content_length: ++ if (ch == ' ') break; ++ h_state = h_content_length_num; ++ /* fall through */ ++ ++ case h_content_length_num: ++ { ++ uint64_t t; ++ ++ if (ch == ' ') { ++ h_state = h_content_length_ws; ++ break; ++ } ++ ++ if (UNLIKELY(!IS_NUM(ch))) { ++ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); ++ parser->header_state = h_state; ++ goto error; ++ } ++ ++ t = parser->content_length; ++ t *= 10; ++ t += ch - '0'; ++ ++ /* Overflow? Test against a conservative limit for simplicity. */ ++ if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { ++ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); ++ parser->header_state = h_state; ++ goto error; ++ } ++ ++ parser->content_length = t; ++ break; ++ } ++ ++ case h_content_length_ws: ++ if (ch == ' ') break; ++ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); ++ parser->header_state = h_state; ++ goto error; ++ ++ /* Transfer-Encoding: chunked */ ++ case h_matching_transfer_encoding_token_start: ++ /* looking for 'Transfer-Encoding: chunked' */ ++ if ('c' == c) { ++ h_state = h_matching_transfer_encoding_chunked; ++ } else if (STRICT_TOKEN(c)) { ++ /* TODO(indutny): similar code below does this, but why? ++ * At the very least it seems to be inconsistent given that ++ * h_matching_transfer_encoding_token does not check for ++ * `STRICT_TOKEN` ++ */ ++ h_state = h_matching_transfer_encoding_token; ++ } else if (c == ' ' || c == '\t') { ++ /* Skip lws */ ++ } else { ++ h_state = h_general; ++ } ++ break; ++ ++ case h_matching_transfer_encoding_chunked: ++ parser->index++; ++ if (parser->index > sizeof(CHUNKED)-1 ++ || c != CHUNKED[parser->index]) { ++ h_state = h_matching_transfer_encoding_token; ++ } else if (parser->index == sizeof(CHUNKED)-2) { ++ h_state = h_transfer_encoding_chunked; ++ } ++ break; ++ ++ case h_matching_transfer_encoding_token: ++ if (ch == ',') { ++ h_state = h_matching_transfer_encoding_token_start; ++ parser->index = 0; ++ } ++ break; ++ ++ case h_matching_connection_token_start: ++ /* looking for 'Connection: keep-alive' */ ++ if (c == 'k') { ++ h_state = h_matching_connection_keep_alive; ++ /* looking for 'Connection: close' */ ++ } else if (c == 'c') { ++ h_state = h_matching_connection_close; ++ } else if (c == 'u') { ++ h_state = h_matching_connection_upgrade; ++ } else if (STRICT_TOKEN(c)) { ++ h_state = h_matching_connection_token; ++ } else if (c == ' ' || c == '\t') { ++ /* Skip lws */ ++ } else { ++ h_state = h_general; ++ } ++ break; ++ ++ /* looking for 'Connection: keep-alive' */ ++ case h_matching_connection_keep_alive: ++ parser->index++; ++ if (parser->index > sizeof(KEEP_ALIVE)-1 ++ || c != KEEP_ALIVE[parser->index]) { ++ h_state = h_matching_connection_token; ++ } else if (parser->index == sizeof(KEEP_ALIVE)-2) { ++ h_state = h_connection_keep_alive; ++ } ++ break; ++ ++ /* looking for 'Connection: close' */ ++ case h_matching_connection_close: ++ parser->index++; ++ if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { ++ h_state = h_matching_connection_token; ++ } else if (parser->index == sizeof(CLOSE)-2) { ++ h_state = h_connection_close; ++ } ++ break; ++ ++ /* looking for 'Connection: upgrade' */ ++ case h_matching_connection_upgrade: ++ parser->index++; ++ if (parser->index > sizeof(UPGRADE) - 1 || ++ c != UPGRADE[parser->index]) { ++ h_state = h_matching_connection_token; ++ } else if (parser->index == sizeof(UPGRADE)-2) { ++ h_state = h_connection_upgrade; ++ } ++ break; ++ ++ case h_matching_connection_token: ++ if (ch == ',') { ++ h_state = h_matching_connection_token_start; ++ parser->index = 0; ++ } ++ break; ++ ++ case h_transfer_encoding_chunked: ++ if (ch != ' ') h_state = h_matching_transfer_encoding_token; ++ break; ++ ++ case h_connection_keep_alive: ++ case h_connection_close: ++ case h_connection_upgrade: ++ if (ch == ',') { ++ if (h_state == h_connection_keep_alive) { ++ parser->flags |= F_CONNECTION_KEEP_ALIVE; ++ } else if (h_state == h_connection_close) { ++ parser->flags |= F_CONNECTION_CLOSE; ++ } else if (h_state == h_connection_upgrade) { ++ parser->flags |= F_CONNECTION_UPGRADE; ++ } ++ h_state = h_matching_connection_token_start; ++ parser->index = 0; ++ } else if (ch != ' ') { ++ h_state = h_matching_connection_token; ++ } ++ break; ++ ++ default: ++ UPDATE_STATE(s_header_value); ++ h_state = h_general; ++ break; ++ } ++ } ++ parser->header_state = h_state; ++ ++ if (p == data + len) ++ --p; ++ ++ COUNT_HEADER_SIZE(p - start); ++ break; ++ } ++ ++ case s_header_almost_done: ++ { ++ if (UNLIKELY(ch != LF)) { ++ SET_ERRNO(HPE_LF_EXPECTED); ++ goto error; ++ } ++ ++ UPDATE_STATE(s_header_value_lws); ++ break; ++ } ++ ++ case s_header_value_lws: ++ { ++ if (ch == ' ' || ch == '\t') { ++ if (parser->header_state == h_content_length_num) { ++ /* treat obsolete line folding as space */ ++ parser->header_state = h_content_length_ws; ++ } ++ UPDATE_STATE(s_header_value_start); ++ REEXECUTE(); ++ } ++ ++ /* finished the header */ ++ switch (parser->header_state) { ++ case h_connection_keep_alive: ++ parser->flags |= F_CONNECTION_KEEP_ALIVE; ++ break; ++ case h_connection_close: ++ parser->flags |= F_CONNECTION_CLOSE; ++ break; ++ case h_transfer_encoding_chunked: ++ parser->flags |= F_CHUNKED; ++ break; ++ case h_connection_upgrade: ++ parser->flags |= F_CONNECTION_UPGRADE; ++ break; ++ default: ++ break; ++ } ++ ++ UPDATE_STATE(s_header_field_start); ++ REEXECUTE(); ++ } ++ ++ case s_header_value_discard_ws_almost_done: ++ { ++ STRICT_CHECK(ch != LF); ++ UPDATE_STATE(s_header_value_discard_lws); ++ break; ++ } ++ ++ case s_header_value_discard_lws: ++ { ++ if (ch == ' ' || ch == '\t') { ++ UPDATE_STATE(s_header_value_discard_ws); ++ break; ++ } else { ++ switch (parser->header_state) { ++ case h_connection_keep_alive: ++ parser->flags |= F_CONNECTION_KEEP_ALIVE; ++ break; ++ case h_connection_close: ++ parser->flags |= F_CONNECTION_CLOSE; ++ break; ++ case h_connection_upgrade: ++ parser->flags |= F_CONNECTION_UPGRADE; ++ break; ++ case h_transfer_encoding_chunked: ++ parser->flags |= F_CHUNKED; ++ break; ++ case h_content_length: ++ /* do not allow empty content length */ ++ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); ++ goto error; ++ break; ++ default: ++ break; ++ } ++ ++ /* header value was empty */ ++ MARK(header_value); ++ UPDATE_STATE(s_header_field_start); ++ CALLBACK_DATA_NOADVANCE(header_value); ++ REEXECUTE(); ++ } ++ } ++ ++ case s_headers_almost_done: ++ { ++ STRICT_CHECK(ch != LF); ++ ++ if (parser->flags & F_TRAILING) { ++ /* End of a chunked request */ ++ UPDATE_STATE(s_message_done); ++ CALLBACK_NOTIFY_NOADVANCE(chunk_complete); ++ REEXECUTE(); ++ } ++ ++ /* Cannot us transfer-encoding and a content-length header together ++ per the HTTP specification. (RFC 7230 Section 3.3.3) */ ++ if ((parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) && ++ (parser->flags & F_CONTENTLENGTH)) { ++ /* Allow it for lenient parsing as long as `Transfer-Encoding` is ++ * not `chunked` ++ */ ++ if (!lenient || (parser->flags & F_CHUNKED)) { ++ SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); ++ goto error; ++ } ++ } ++ ++ UPDATE_STATE(s_headers_done); ++ ++ /* Set this here so that on_headers_complete() callbacks can see it */ ++ if ((parser->flags & F_UPGRADE) && ++ (parser->flags & F_CONNECTION_UPGRADE)) { ++ /* For responses, "Upgrade: foo" and "Connection: upgrade" are ++ * mandatory only when it is a 101 Switching Protocols response, ++ * otherwise it is purely informational, to announce support. ++ */ ++ parser->upgrade = ++ (parser->type == HTTP_REQUEST || parser->status_code == 101); ++ } else { ++ parser->upgrade = (parser->method == HTTP_CONNECT); ++ } ++ ++ /* Here we call the headers_complete callback. This is somewhat ++ * different than other callbacks because if the user returns 1, we ++ * will interpret that as saying that this message has no body. This ++ * is needed for the annoying case of recieving a response to a HEAD ++ * request. ++ * ++ * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so ++ * we have to simulate it by handling a change in errno below. ++ */ ++ if (settings->on_headers_complete) { ++ switch (settings->on_headers_complete(parser)) { ++ case 0: ++ break; ++ ++ case 2: ++ parser->upgrade = 1; ++ ++ /* fall through */ ++ case 1: ++ parser->flags |= F_SKIPBODY; ++ break; ++ ++ default: ++ SET_ERRNO(HPE_CB_headers_complete); ++ RETURN(p - data); /* Error */ ++ } ++ } ++ ++ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { ++ RETURN(p - data); ++ } ++ ++ REEXECUTE(); ++ } ++ ++ case s_headers_done: ++ { ++ int hasBody; ++ STRICT_CHECK(ch != LF); ++ ++ parser->nread = 0; ++ nread = 0; ++ ++ hasBody = parser->flags & F_CHUNKED || ++ (parser->content_length > 0 && parser->content_length != ULLONG_MAX); ++ if (parser->upgrade && (parser->method == HTTP_CONNECT || ++ (parser->flags & F_SKIPBODY) || !hasBody)) { ++ /* Exit, the rest of the message is in a different protocol. */ ++ UPDATE_STATE(NEW_MESSAGE()); ++ CALLBACK_NOTIFY(message_complete); ++ RETURN((p - data) + 1); ++ } ++ ++ if (parser->flags & F_SKIPBODY) { ++ UPDATE_STATE(NEW_MESSAGE()); ++ CALLBACK_NOTIFY(message_complete); ++ } else if (parser->flags & F_CHUNKED) { ++ /* chunked encoding - ignore Content-Length header, ++ * prepare for a chunk */ ++ UPDATE_STATE(s_chunk_size_start); ++ } else if (parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) { ++ if (parser->type == HTTP_REQUEST && !lenient) { ++ /* RFC 7230 3.3.3 */ ++ ++ /* If a Transfer-Encoding header field ++ * is present in a request and the chunked transfer coding is not ++ * the final encoding, the message body length cannot be determined ++ * reliably; the server MUST respond with the 400 (Bad Request) ++ * status code and then close the connection. ++ */ ++ SET_ERRNO(HPE_INVALID_TRANSFER_ENCODING); ++ RETURN(p - data); /* Error */ ++ } else { ++ /* RFC 7230 3.3.3 */ ++ ++ /* If a Transfer-Encoding header field is present in a response and ++ * the chunked transfer coding is not the final encoding, the ++ * message body length is determined by reading the connection until ++ * it is closed by the server. ++ */ ++ UPDATE_STATE(s_body_identity_eof); ++ } ++ } else { ++ if (parser->content_length == 0) { ++ /* Content-Length header given but zero: Content-Length: 0\r\n */ ++ UPDATE_STATE(NEW_MESSAGE()); ++ CALLBACK_NOTIFY(message_complete); ++ } else if (parser->content_length != ULLONG_MAX) { ++ /* Content-Length header given and non-zero */ ++ UPDATE_STATE(s_body_identity); ++ } else { ++ if (!http_message_needs_eof(parser)) { ++ /* Assume content-length 0 - read the next */ ++ UPDATE_STATE(NEW_MESSAGE()); ++ CALLBACK_NOTIFY(message_complete); ++ } else { ++ /* Read body until EOF */ ++ UPDATE_STATE(s_body_identity_eof); ++ } ++ } ++ } ++ ++ break; ++ } ++ ++ case s_body_identity: ++ { ++ uint64_t to_read = MIN(parser->content_length, ++ (uint64_t) ((data + len) - p)); ++ ++ assert(parser->content_length != 0 ++ && parser->content_length != ULLONG_MAX); ++ ++ /* The difference between advancing content_length and p is because ++ * the latter will automaticaly advance on the next loop iteration. ++ * Further, if content_length ends up at 0, we want to see the last ++ * byte again for our message complete callback. ++ */ ++ MARK(body); ++ parser->content_length -= to_read; ++ p += to_read - 1; ++ ++ if (parser->content_length == 0) { ++ UPDATE_STATE(s_message_done); ++ ++ /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. ++ * ++ * The alternative to doing this is to wait for the next byte to ++ * trigger the data callback, just as in every other case. The ++ * problem with this is that this makes it difficult for the test ++ * harness to distinguish between complete-on-EOF and ++ * complete-on-length. It's not clear that this distinction is ++ * important for applications, but let's keep it for now. ++ */ ++ CALLBACK_DATA_(body, p - body_mark + 1, p - data); ++ REEXECUTE(); ++ } ++ ++ break; ++ } ++ ++ /* read until EOF */ ++ case s_body_identity_eof: ++ MARK(body); ++ p = data + len - 1; ++ ++ break; ++ ++ case s_message_done: ++ UPDATE_STATE(NEW_MESSAGE()); ++ CALLBACK_NOTIFY(message_complete); ++ if (parser->upgrade) { ++ /* Exit, the rest of the message is in a different protocol. */ ++ RETURN((p - data) + 1); ++ } ++ break; ++ ++ case s_chunk_size_start: ++ { ++ assert(nread == 1); ++ assert(parser->flags & F_CHUNKED); ++ ++ unhex_val = unhex[(unsigned char)ch]; ++ if (UNLIKELY(unhex_val == -1)) { ++ SET_ERRNO(HPE_INVALID_CHUNK_SIZE); ++ goto error; ++ } ++ ++ parser->content_length = unhex_val; ++ UPDATE_STATE(s_chunk_size); ++ break; ++ } ++ ++ case s_chunk_size: ++ { ++ uint64_t t; ++ ++ assert(parser->flags & F_CHUNKED); ++ ++ if (ch == CR) { ++ UPDATE_STATE(s_chunk_size_almost_done); ++ break; ++ } ++ ++ unhex_val = unhex[(unsigned char)ch]; ++ ++ if (unhex_val == -1) { ++ if (ch == ';' || ch == ' ') { ++ UPDATE_STATE(s_chunk_parameters); ++ break; ++ } ++ ++ SET_ERRNO(HPE_INVALID_CHUNK_SIZE); ++ goto error; ++ } ++ ++ t = parser->content_length; ++ t *= 16; ++ t += unhex_val; ++ ++ /* Overflow? Test against a conservative limit for simplicity. */ ++ if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { ++ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); ++ goto error; ++ } ++ ++ parser->content_length = t; ++ break; ++ } ++ ++ case s_chunk_parameters: ++ { ++ assert(parser->flags & F_CHUNKED); ++ /* just ignore this shit. TODO check for overflow */ ++ if (ch == CR) { ++ UPDATE_STATE(s_chunk_size_almost_done); ++ break; ++ } ++ break; ++ } ++ ++ case s_chunk_size_almost_done: ++ { ++ assert(parser->flags & F_CHUNKED); ++ STRICT_CHECK(ch != LF); ++ ++ parser->nread = 0; ++ nread = 0; ++ ++ if (parser->content_length == 0) { ++ parser->flags |= F_TRAILING; ++ UPDATE_STATE(s_header_field_start); ++ } else { ++ UPDATE_STATE(s_chunk_data); ++ } ++ CALLBACK_NOTIFY(chunk_header); ++ break; ++ } ++ ++ case s_chunk_data: ++ { ++ uint64_t to_read = MIN(parser->content_length, ++ (uint64_t) ((data + len) - p)); ++ ++ assert(parser->flags & F_CHUNKED); ++ assert(parser->content_length != 0 ++ && parser->content_length != ULLONG_MAX); ++ ++ /* See the explanation in s_body_identity for why the content ++ * length and data pointers are managed this way. ++ */ ++ MARK(body); ++ parser->content_length -= to_read; ++ p += to_read - 1; ++ ++ if (parser->content_length == 0) { ++ UPDATE_STATE(s_chunk_data_almost_done); ++ } ++ ++ break; ++ } ++ ++ case s_chunk_data_almost_done: ++ assert(parser->flags & F_CHUNKED); ++ assert(parser->content_length == 0); ++ STRICT_CHECK(ch != CR); ++ UPDATE_STATE(s_chunk_data_done); ++ CALLBACK_DATA(body); ++ break; ++ ++ case s_chunk_data_done: ++ assert(parser->flags & F_CHUNKED); ++ STRICT_CHECK(ch != LF); ++ parser->nread = 0; ++ nread = 0; ++ UPDATE_STATE(s_chunk_size_start); ++ CALLBACK_NOTIFY(chunk_complete); ++ break; ++ ++ default: ++ assert(0 && "unhandled state"); ++ SET_ERRNO(HPE_INVALID_INTERNAL_STATE); ++ goto error; ++ } ++ } ++ ++ /* Run callbacks for any marks that we have leftover after we ran out of ++ * bytes. There should be at most one of these set, so it's OK to invoke ++ * them in series (unset marks will not result in callbacks). ++ * ++ * We use the NOADVANCE() variety of callbacks here because 'p' has already ++ * overflowed 'data' and this allows us to correct for the off-by-one that ++ * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' ++ * value that's in-bounds). ++ */ ++ ++ assert(((header_field_mark ? 1 : 0) + ++ (header_value_mark ? 1 : 0) + ++ (url_mark ? 1 : 0) + ++ (body_mark ? 1 : 0) + ++ (status_mark ? 1 : 0)) <= 1); ++ ++ CALLBACK_DATA_NOADVANCE(header_field); ++ CALLBACK_DATA_NOADVANCE(header_value); ++ CALLBACK_DATA_NOADVANCE(url); ++ CALLBACK_DATA_NOADVANCE(body); ++ CALLBACK_DATA_NOADVANCE(status); ++ ++ RETURN(len); ++ ++error: ++ if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { ++ SET_ERRNO(HPE_UNKNOWN); ++ } ++ ++ RETURN(p - data); ++} ++ ++ ++/* Does the parser need to see an EOF to find the end of the message? */ ++int ++http_message_needs_eof (const http_parser *parser) ++{ ++ if (parser->type == HTTP_REQUEST) { ++ return 0; ++ } ++ ++ /* See RFC 2616 section 4.4 */ ++ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ ++ parser->status_code == 204 || /* No Content */ ++ parser->status_code == 304 || /* Not Modified */ ++ parser->flags & F_SKIPBODY) { /* response to a HEAD request */ ++ return 0; ++ } ++ ++ /* RFC 7230 3.3.3, see `s_headers_almost_done` */ ++ if ((parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) && ++ (parser->flags & F_CHUNKED) == 0) { ++ return 1; ++ } ++ ++ if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++int ++http_should_keep_alive (const http_parser *parser) ++{ ++ if (parser->http_major > 0 && parser->http_minor > 0) { ++ /* HTTP/1.1 */ ++ if (parser->flags & F_CONNECTION_CLOSE) { ++ return 0; ++ } ++ } else { ++ /* HTTP/1.0 or earlier */ ++ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { ++ return 0; ++ } ++ } ++ ++ return !http_message_needs_eof(parser); ++} ++ ++ ++const char * ++http_method_str (enum http_method m) ++{ ++ return ELEM_AT(method_strings, m, ""); ++} ++ ++const char * ++http_status_str (enum http_status s) ++{ ++ switch (s) { ++#define XX(num, name, string) case HTTP_STATUS_##name: return #string; ++ HTTP_STATUS_MAP(XX) ++#undef XX ++ default: return ""; ++ } ++} ++ ++void ++http_parser_init (http_parser *parser, enum http_parser_type t) ++{ ++ void *data = parser->data; /* preserve application data */ ++ memset(parser, 0, sizeof(*parser)); ++ parser->data = data; ++ parser->type = t; ++ parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); ++ parser->http_errno = HPE_OK; ++} ++ ++void ++http_parser_settings_init(http_parser_settings *settings) ++{ ++ memset(settings, 0, sizeof(*settings)); ++} ++ ++const char * ++http_errno_name(enum http_errno err) { ++ assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); ++ return http_strerror_tab[err].name; ++} ++ ++const char * ++http_errno_description(enum http_errno err) { ++ assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); ++ return http_strerror_tab[err].description; ++} ++ ++static enum http_host_state ++http_parse_host_char(enum http_host_state s, const char ch) { ++ switch(s) { ++ case s_http_userinfo: ++ case s_http_userinfo_start: ++ if (ch == '@') { ++ return s_http_host_start; ++ } ++ ++ if (IS_USERINFO_CHAR(ch)) { ++ return s_http_userinfo; ++ } ++ break; ++ ++ case s_http_host_start: ++ if (ch == '[') { ++ return s_http_host_v6_start; ++ } ++ ++ if (IS_HOST_CHAR(ch)) { ++ return s_http_host; ++ } ++ ++ break; ++ ++ case s_http_host: ++ if (IS_HOST_CHAR(ch)) { ++ return s_http_host; ++ } ++ ++ /* fall through */ ++ case s_http_host_v6_end: ++ if (ch == ':') { ++ return s_http_host_port_start; ++ } ++ ++ break; ++ ++ case s_http_host_v6: ++ if (ch == ']') { ++ return s_http_host_v6_end; ++ } ++ ++ /* fall through */ ++ case s_http_host_v6_start: ++ if (IS_HEX(ch) || ch == ':' || ch == '.') { ++ return s_http_host_v6; ++ } ++ ++ if (s == s_http_host_v6 && ch == '%') { ++ return s_http_host_v6_zone_start; ++ } ++ break; ++ ++ case s_http_host_v6_zone: ++ if (ch == ']') { ++ return s_http_host_v6_end; ++ } ++ ++ /* fall through */ ++ case s_http_host_v6_zone_start: ++ /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ ++ if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || ++ ch == '~') { ++ return s_http_host_v6_zone; ++ } ++ break; ++ ++ case s_http_host_port: ++ case s_http_host_port_start: ++ if (IS_NUM(ch)) { ++ return s_http_host_port; ++ } ++ ++ break; ++ ++ default: ++ break; ++ } ++ return s_http_host_dead; ++} ++ ++static int ++http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { ++ enum http_host_state s; ++ ++ const char *p; ++ size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; ++ ++ assert(u->field_set & (1 << UF_HOST)); ++ ++ u->field_data[UF_HOST].len = 0; ++ ++ s = found_at ? s_http_userinfo_start : s_http_host_start; ++ ++ for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { ++ enum http_host_state new_s = http_parse_host_char(s, *p); ++ ++ if (new_s == s_http_host_dead) { ++ return 1; ++ } ++ ++ switch(new_s) { ++ case s_http_host: ++ if (s != s_http_host) { ++ u->field_data[UF_HOST].off = (uint16_t)(p - buf); ++ } ++ u->field_data[UF_HOST].len++; ++ break; ++ ++ case s_http_host_v6: ++ if (s != s_http_host_v6) { ++ u->field_data[UF_HOST].off = (uint16_t)(p - buf); ++ } ++ u->field_data[UF_HOST].len++; ++ break; ++ ++ case s_http_host_v6_zone_start: ++ case s_http_host_v6_zone: ++ u->field_data[UF_HOST].len++; ++ break; ++ ++ case s_http_host_port: ++ if (s != s_http_host_port) { ++ u->field_data[UF_PORT].off = (uint16_t)(p - buf); ++ u->field_data[UF_PORT].len = 0; ++ u->field_set |= (1 << UF_PORT); ++ } ++ u->field_data[UF_PORT].len++; ++ break; ++ ++ case s_http_userinfo: ++ if (s != s_http_userinfo) { ++ u->field_data[UF_USERINFO].off = (uint16_t)(p - buf); ++ u->field_data[UF_USERINFO].len = 0; ++ u->field_set |= (1 << UF_USERINFO); ++ } ++ u->field_data[UF_USERINFO].len++; ++ break; ++ ++ default: ++ break; ++ } ++ s = new_s; ++ } ++ ++ /* Make sure we don't end somewhere unexpected */ ++ switch (s) { ++ case s_http_host_start: ++ case s_http_host_v6_start: ++ case s_http_host_v6: ++ case s_http_host_v6_zone_start: ++ case s_http_host_v6_zone: ++ case s_http_userinfo: ++ case s_http_userinfo_start: ++ return 1; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++void ++http_parser_url_init(struct http_parser_url *u) { ++ memset(u, 0, sizeof(*u)); ++} ++ ++int ++http_parser_parse_url(const char *buf, size_t buflen, int is_connect, ++ struct http_parser_url *u) ++{ ++ enum state s; ++ const char *p; ++ enum http_parser_url_fields uf, old_uf; ++ int found_at = 0; ++ ++ if (buflen == 0) { ++ return 1; ++ } ++ ++ u->port = u->field_set = 0; ++ s = is_connect ? s_req_server_start : s_req_spaces_before_url; ++ old_uf = UF_MAX; ++ ++ for (p = buf; p < buf + buflen; p++) { ++ s = parse_url_char(s, *p); ++ ++ /* Figure out the next field that we're operating on */ ++ switch (s) { ++ case s_dead: ++ return 1; ++ ++ /* Skip delimeters */ ++ case s_req_schema_slash: ++ case s_req_schema_slash_slash: ++ case s_req_server_start: ++ case s_req_query_string_start: ++ case s_req_fragment_start: ++ continue; ++ ++ case s_req_schema: ++ uf = UF_SCHEMA; ++ break; ++ ++ case s_req_server_with_at: ++ found_at = 1; ++ ++ /* fall through */ ++ case s_req_server: ++ uf = UF_HOST; ++ break; ++ ++ case s_req_path: ++ uf = UF_PATH; ++ break; ++ ++ case s_req_query_string: ++ uf = UF_QUERY; ++ break; ++ ++ case s_req_fragment: ++ uf = UF_FRAGMENT; ++ break; ++ ++ default: ++ assert(!"Unexpected state"); ++ return 1; ++ } ++ ++ /* Nothing's changed; soldier on */ ++ if (uf == old_uf) { ++ u->field_data[uf].len++; ++ continue; ++ } ++ ++ u->field_data[uf].off = (uint16_t)(p - buf); ++ u->field_data[uf].len = 1; ++ ++ u->field_set |= (1 << uf); ++ old_uf = uf; ++ } ++ ++ /* host must be present if there is a schema */ ++ /* parsing http:///toto will fail */ ++ if ((u->field_set & (1 << UF_SCHEMA)) && ++ (u->field_set & (1 << UF_HOST)) == 0) { ++ return 1; ++ } ++ ++ if (u->field_set & (1 << UF_HOST)) { ++ if (http_parse_host(buf, u, found_at) != 0) { ++ return 1; ++ } ++ } ++ ++ /* CONNECT requests can only contain "hostname:port" */ ++ if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { ++ return 1; ++ } ++ ++ if (u->field_set & (1 << UF_PORT)) { ++ uint16_t off; ++ uint16_t len; ++ const char* p; ++ const char* end; ++ unsigned long v; ++ ++ off = u->field_data[UF_PORT].off; ++ len = u->field_data[UF_PORT].len; ++ end = buf + off + len; ++ ++ /* NOTE: The characters are already validated and are in the [0-9] range */ ++ assert(off + len <= buflen && "Port number overflow"); ++ v = 0; ++ for (p = buf + off; p < end; p++) { ++ v *= 10; ++ v += *p - '0'; ++ ++ /* Ports have a max value of 2^16 */ ++ if (v > 0xffff) { ++ return 1; ++ } ++ } ++ ++ u->port = (uint16_t) v; ++ } ++ ++ return 0; ++} ++ ++void ++http_parser_pause(http_parser *parser, int paused) { ++ /* Users should only be pausing/unpausing a parser that is not in an error ++ * state. In non-debug builds, there's not much that we can do about this ++ * other than ignore it. ++ */ ++ if (HTTP_PARSER_ERRNO(parser) == HPE_OK || ++ HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { ++ uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */ ++ SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); ++ } else { ++ assert(0 && "Attempting to pause parser in error state"); ++ } ++} ++ ++int ++http_body_is_final(const struct http_parser *parser) { ++ return parser->state == s_message_done; ++} ++ ++unsigned long ++http_parser_version(void) { ++ return HTTP_PARSER_VERSION_MAJOR * 0x10000 | ++ HTTP_PARSER_VERSION_MINOR * 0x00100 | ++ HTTP_PARSER_VERSION_PATCH * 0x00001; ++} ++ ++void ++http_parser_set_max_header_size(uint32_t size) { ++ max_header_size = size; ++} +diff --git a/src/third_party/http_parser/http_parser.h b/src/third_party/http_parser/http_parser.h +new file mode 100644 +index 0000000..df88252 +--- /dev/null ++++ b/src/third_party/http_parser/http_parser.h +@@ -0,0 +1,443 @@ ++/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++#ifndef http_parser_h ++#define http_parser_h ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* Also update SONAME in the Makefile whenever you change these. */ ++#define HTTP_PARSER_VERSION_MAJOR 2 ++#define HTTP_PARSER_VERSION_MINOR 9 ++#define HTTP_PARSER_VERSION_PATCH 4 ++ ++#include ++#if defined(_WIN32) && !defined(__MINGW32__) && \ ++ (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) ++#include ++typedef __int8 int8_t; ++typedef unsigned __int8 uint8_t; ++typedef __int16 int16_t; ++typedef unsigned __int16 uint16_t; ++typedef __int32 int32_t; ++typedef unsigned __int32 uint32_t; ++typedef __int64 int64_t; ++typedef unsigned __int64 uint64_t; ++#else ++#include ++#endif ++ ++/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run ++ * faster ++ */ ++#ifndef HTTP_PARSER_STRICT ++# define HTTP_PARSER_STRICT 1 ++#endif ++ ++/* Maximium header size allowed. If the macro is not defined ++ * before including this header then the default is used. To ++ * change the maximum header size, define the macro in the build ++ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove ++ * the effective limit on the size of the header, define the macro ++ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) ++ */ ++#ifndef HTTP_MAX_HEADER_SIZE ++# define HTTP_MAX_HEADER_SIZE (80*1024) ++#endif ++ ++typedef struct http_parser http_parser; ++typedef struct http_parser_settings http_parser_settings; ++ ++ ++/* Callbacks should return non-zero to indicate an error. The parser will ++ * then halt execution. ++ * ++ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser ++ * returning '1' from on_headers_complete will tell the parser that it ++ * should not expect a body. This is used when receiving a response to a ++ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: ++ * chunked' headers that indicate the presence of a body. ++ * ++ * Returning `2` from on_headers_complete will tell parser that it should not ++ * expect neither a body nor any futher responses on this connection. This is ++ * useful for handling responses to a CONNECT request which may not contain ++ * `Upgrade` or `Connection: upgrade` headers. ++ * ++ * http_data_cb does not return data chunks. It will be called arbitrarily ++ * many times for each string. E.G. you might get 10 callbacks for "on_url" ++ * each providing just a few characters more data. ++ */ ++typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); ++typedef int (*http_cb) (http_parser*); ++ ++ ++/* Status Codes */ ++#define HTTP_STATUS_MAP(XX) \ ++ XX(100, CONTINUE, Continue) \ ++ XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \ ++ XX(102, PROCESSING, Processing) \ ++ XX(200, OK, OK) \ ++ XX(201, CREATED, Created) \ ++ XX(202, ACCEPTED, Accepted) \ ++ XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \ ++ XX(204, NO_CONTENT, No Content) \ ++ XX(205, RESET_CONTENT, Reset Content) \ ++ XX(206, PARTIAL_CONTENT, Partial Content) \ ++ XX(207, MULTI_STATUS, Multi-Status) \ ++ XX(208, ALREADY_REPORTED, Already Reported) \ ++ XX(226, IM_USED, IM Used) \ ++ XX(300, MULTIPLE_CHOICES, Multiple Choices) \ ++ XX(301, MOVED_PERMANENTLY, Moved Permanently) \ ++ XX(302, FOUND, Found) \ ++ XX(303, SEE_OTHER, See Other) \ ++ XX(304, NOT_MODIFIED, Not Modified) \ ++ XX(305, USE_PROXY, Use Proxy) \ ++ XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \ ++ XX(308, PERMANENT_REDIRECT, Permanent Redirect) \ ++ XX(400, BAD_REQUEST, Bad Request) \ ++ XX(401, UNAUTHORIZED, Unauthorized) \ ++ XX(402, PAYMENT_REQUIRED, Payment Required) \ ++ XX(403, FORBIDDEN, Forbidden) \ ++ XX(404, NOT_FOUND, Not Found) \ ++ XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \ ++ XX(406, NOT_ACCEPTABLE, Not Acceptable) \ ++ XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \ ++ XX(408, REQUEST_TIMEOUT, Request Timeout) \ ++ XX(409, CONFLICT, Conflict) \ ++ XX(410, GONE, Gone) \ ++ XX(411, LENGTH_REQUIRED, Length Required) \ ++ XX(412, PRECONDITION_FAILED, Precondition Failed) \ ++ XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \ ++ XX(414, URI_TOO_LONG, URI Too Long) \ ++ XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \ ++ XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \ ++ XX(417, EXPECTATION_FAILED, Expectation Failed) \ ++ XX(421, MISDIRECTED_REQUEST, Misdirected Request) \ ++ XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \ ++ XX(423, LOCKED, Locked) \ ++ XX(424, FAILED_DEPENDENCY, Failed Dependency) \ ++ XX(426, UPGRADE_REQUIRED, Upgrade Required) \ ++ XX(428, PRECONDITION_REQUIRED, Precondition Required) \ ++ XX(429, TOO_MANY_REQUESTS, Too Many Requests) \ ++ XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \ ++ XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \ ++ XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \ ++ XX(501, NOT_IMPLEMENTED, Not Implemented) \ ++ XX(502, BAD_GATEWAY, Bad Gateway) \ ++ XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \ ++ XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \ ++ XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \ ++ XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \ ++ XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \ ++ XX(508, LOOP_DETECTED, Loop Detected) \ ++ XX(510, NOT_EXTENDED, Not Extended) \ ++ XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \ ++ ++enum http_status ++ { ++#define XX(num, name, string) HTTP_STATUS_##name = num, ++ HTTP_STATUS_MAP(XX) ++#undef XX ++ }; ++ ++ ++/* Request Methods */ ++#define HTTP_METHOD_MAP(XX) \ ++ XX(0, DELETE, DELETE) \ ++ XX(1, GET, GET) \ ++ XX(2, HEAD, HEAD) \ ++ XX(3, POST, POST) \ ++ XX(4, PUT, PUT) \ ++ /* pathological */ \ ++ XX(5, CONNECT, CONNECT) \ ++ XX(6, OPTIONS, OPTIONS) \ ++ XX(7, TRACE, TRACE) \ ++ /* WebDAV */ \ ++ XX(8, COPY, COPY) \ ++ XX(9, LOCK, LOCK) \ ++ XX(10, MKCOL, MKCOL) \ ++ XX(11, MOVE, MOVE) \ ++ XX(12, PROPFIND, PROPFIND) \ ++ XX(13, PROPPATCH, PROPPATCH) \ ++ XX(14, SEARCH, SEARCH) \ ++ XX(15, UNLOCK, UNLOCK) \ ++ XX(16, BIND, BIND) \ ++ XX(17, REBIND, REBIND) \ ++ XX(18, UNBIND, UNBIND) \ ++ XX(19, ACL, ACL) \ ++ /* subversion */ \ ++ XX(20, REPORT, REPORT) \ ++ XX(21, MKACTIVITY, MKACTIVITY) \ ++ XX(22, CHECKOUT, CHECKOUT) \ ++ XX(23, MERGE, MERGE) \ ++ /* upnp */ \ ++ XX(24, MSEARCH, M-SEARCH) \ ++ XX(25, NOTIFY, NOTIFY) \ ++ XX(26, SUBSCRIBE, SUBSCRIBE) \ ++ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ ++ /* RFC-5789 */ \ ++ XX(28, PATCH, PATCH) \ ++ XX(29, PURGE, PURGE) \ ++ /* CalDAV */ \ ++ XX(30, MKCALENDAR, MKCALENDAR) \ ++ /* RFC-2068, section 19.6.1.2 */ \ ++ XX(31, LINK, LINK) \ ++ XX(32, UNLINK, UNLINK) \ ++ /* icecast */ \ ++ XX(33, SOURCE, SOURCE) \ ++ ++enum http_method ++ { ++#define XX(num, name, string) HTTP_##name = num, ++ HTTP_METHOD_MAP(XX) ++#undef XX ++ }; ++ ++ ++enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; ++ ++ ++/* Flag values for http_parser.flags field */ ++enum flags ++ { F_CHUNKED = 1 << 0 ++ , F_CONNECTION_KEEP_ALIVE = 1 << 1 ++ , F_CONNECTION_CLOSE = 1 << 2 ++ , F_CONNECTION_UPGRADE = 1 << 3 ++ , F_TRAILING = 1 << 4 ++ , F_UPGRADE = 1 << 5 ++ , F_SKIPBODY = 1 << 6 ++ , F_CONTENTLENGTH = 1 << 7 ++ , F_TRANSFER_ENCODING = 1 << 8 /* Never set in http_parser.flags */ ++ }; ++ ++ ++/* Map for errno-related constants ++ * ++ * The provided argument should be a macro that takes 2 arguments. ++ */ ++#define HTTP_ERRNO_MAP(XX) \ ++ /* No error */ \ ++ XX(OK, "success") \ ++ \ ++ /* Callback-related errors */ \ ++ XX(CB_message_begin, "the on_message_begin callback failed") \ ++ XX(CB_url, "the on_url callback failed") \ ++ XX(CB_header_field, "the on_header_field callback failed") \ ++ XX(CB_header_value, "the on_header_value callback failed") \ ++ XX(CB_headers_complete, "the on_headers_complete callback failed") \ ++ XX(CB_body, "the on_body callback failed") \ ++ XX(CB_message_complete, "the on_message_complete callback failed") \ ++ XX(CB_status, "the on_status callback failed") \ ++ XX(CB_chunk_header, "the on_chunk_header callback failed") \ ++ XX(CB_chunk_complete, "the on_chunk_complete callback failed") \ ++ \ ++ /* Parsing-related errors */ \ ++ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ ++ XX(HEADER_OVERFLOW, \ ++ "too many header bytes seen; overflow detected") \ ++ XX(CLOSED_CONNECTION, \ ++ "data received after completed connection: close message") \ ++ XX(INVALID_VERSION, "invalid HTTP version") \ ++ XX(INVALID_STATUS, "invalid HTTP status code") \ ++ XX(INVALID_METHOD, "invalid HTTP method") \ ++ XX(INVALID_URL, "invalid URL") \ ++ XX(INVALID_HOST, "invalid host") \ ++ XX(INVALID_PORT, "invalid port") \ ++ XX(INVALID_PATH, "invalid path") \ ++ XX(INVALID_QUERY_STRING, "invalid query string") \ ++ XX(INVALID_FRAGMENT, "invalid fragment") \ ++ XX(LF_EXPECTED, "LF character expected") \ ++ XX(INVALID_HEADER_TOKEN, "invalid character in header") \ ++ XX(INVALID_CONTENT_LENGTH, \ ++ "invalid character in content-length header") \ ++ XX(UNEXPECTED_CONTENT_LENGTH, \ ++ "unexpected content-length header") \ ++ XX(INVALID_CHUNK_SIZE, \ ++ "invalid character in chunk size header") \ ++ XX(INVALID_CONSTANT, "invalid constant string") \ ++ XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ ++ XX(STRICT, "strict mode assertion failed") \ ++ XX(PAUSED, "parser is paused") \ ++ XX(UNKNOWN, "an unknown error occurred") \ ++ XX(INVALID_TRANSFER_ENCODING, \ ++ "request has invalid transfer-encoding") \ ++ ++ ++/* Define HPE_* values for each errno value above */ ++#define HTTP_ERRNO_GEN(n, s) HPE_##n, ++enum http_errno { ++ HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) ++}; ++#undef HTTP_ERRNO_GEN ++ ++ ++/* Get an http_errno value from an http_parser */ ++#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) ++ ++ ++struct http_parser { ++ /** PRIVATE **/ ++ unsigned int type : 2; /* enum http_parser_type */ ++ unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */ ++ unsigned int state : 7; /* enum state from http_parser.c */ ++ unsigned int header_state : 7; /* enum header_state from http_parser.c */ ++ unsigned int index : 5; /* index into current matcher */ ++ unsigned int extra_flags : 2; ++ unsigned int lenient_http_headers : 1; ++ ++ uint32_t nread; /* # bytes read in various scenarios */ ++ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ ++ ++ /** READ-ONLY **/ ++ unsigned short http_major; ++ unsigned short http_minor; ++ unsigned int status_code : 16; /* responses only */ ++ unsigned int method : 8; /* requests only */ ++ unsigned int http_errno : 7; ++ ++ /* 1 = Upgrade header was present and the parser has exited because of that. ++ * 0 = No upgrade header present. ++ * Should be checked when http_parser_execute() returns in addition to ++ * error checking. ++ */ ++ unsigned int upgrade : 1; ++ ++ /** PUBLIC **/ ++ void *data; /* A pointer to get hook to the "connection" or "socket" object */ ++}; ++ ++ ++struct http_parser_settings { ++ http_cb on_message_begin; ++ http_data_cb on_url; ++ http_data_cb on_status; ++ http_data_cb on_header_field; ++ http_data_cb on_header_value; ++ http_cb on_headers_complete; ++ http_data_cb on_body; ++ http_cb on_message_complete; ++ /* When on_chunk_header is called, the current chunk length is stored ++ * in parser->content_length. ++ */ ++ http_cb on_chunk_header; ++ http_cb on_chunk_complete; ++}; ++ ++ ++enum http_parser_url_fields ++ { UF_SCHEMA = 0 ++ , UF_HOST = 1 ++ , UF_PORT = 2 ++ , UF_PATH = 3 ++ , UF_QUERY = 4 ++ , UF_FRAGMENT = 5 ++ , UF_USERINFO = 6 ++ , UF_MAX = 7 ++ }; ++ ++ ++/* Result structure for http_parser_parse_url(). ++ * ++ * Callers should index into field_data[] with UF_* values iff field_set ++ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and ++ * because we probably have padding left over), we convert any port to ++ * a uint16_t. ++ */ ++struct http_parser_url { ++ uint16_t field_set; /* Bitmask of (1 << UF_*) values */ ++ uint16_t port; /* Converted UF_PORT string */ ++ ++ struct { ++ uint16_t off; /* Offset into buffer in which field starts */ ++ uint16_t len; /* Length of run in buffer */ ++ } field_data[UF_MAX]; ++}; ++ ++ ++/* Returns the library version. Bits 16-23 contain the major version number, ++ * bits 8-15 the minor version number and bits 0-7 the patch level. ++ * Usage example: ++ * ++ * unsigned long version = http_parser_version(); ++ * unsigned major = (version >> 16) & 255; ++ * unsigned minor = (version >> 8) & 255; ++ * unsigned patch = version & 255; ++ * printf("http_parser v%u.%u.%u\n", major, minor, patch); ++ */ ++unsigned long http_parser_version(void); ++ ++void http_parser_init(http_parser *parser, enum http_parser_type type); ++ ++ ++/* Initialize http_parser_settings members to 0 ++ */ ++void http_parser_settings_init(http_parser_settings *settings); ++ ++ ++/* Executes the parser. Returns number of parsed bytes. Sets ++ * `parser->http_errno` on error. */ ++size_t http_parser_execute(http_parser *parser, ++ const http_parser_settings *settings, ++ const char *data, ++ size_t len); ++ ++ ++/* If http_should_keep_alive() in the on_headers_complete or ++ * on_message_complete callback returns 0, then this should be ++ * the last message on the connection. ++ * If you are the server, respond with the "Connection: close" header. ++ * If you are the client, close the connection. ++ */ ++int http_should_keep_alive(const http_parser *parser); ++ ++/* Returns a string version of the HTTP method. */ ++const char *http_method_str(enum http_method m); ++ ++/* Returns a string version of the HTTP status code. */ ++const char *http_status_str(enum http_status s); ++ ++/* Return a string name of the given error */ ++const char *http_errno_name(enum http_errno err); ++ ++/* Return a string description of the given error */ ++const char *http_errno_description(enum http_errno err); ++ ++/* Initialize all http_parser_url members to 0 */ ++void http_parser_url_init(struct http_parser_url *u); ++ ++/* Parse a URL; return nonzero on failure */ ++int http_parser_parse_url(const char *buf, size_t buflen, ++ int is_connect, ++ struct http_parser_url *u); ++ ++/* Pause or un-pause the parser; a nonzero value pauses */ ++void http_parser_pause(http_parser *parser, int paused); ++ ++/* Checks if this is the final chunk of the body. */ ++int http_body_is_final(const http_parser *parser); ++ ++/* Change the maximum header size provided at compile time. */ ++void http_parser_set_max_header_size(uint32_t size); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif +-- +2.33.0 + diff --git a/0003-add-swap-usage-fields-in-shim-stats-and-container-in.patch b/0003-add-swap-usage-fields-in-shim-stats-and-container-in.patch new file mode 100644 index 0000000..964fcd2 --- /dev/null +++ b/0003-add-swap-usage-fields-in-shim-stats-and-container-in.patch @@ -0,0 +1,53 @@ +From 299f54b13a85855540e6d28e9c9bfefb7bc66f14 Mon Sep 17 00:00:00 2001 +From: jikai +Date: Fri, 12 Jan 2024 11:30:34 +0800 +Subject: [PATCH 3/3] add swap usage fields in shim stats and container info + +Signed-off-by: jikai +--- + src/json/schema/container/info.json | 6 ++++++ + src/json/schema/shim/client/runtime-stats.json | 11 +++++++++++ + 2 files changed, 17 insertions(+) + +diff --git a/src/json/schema/container/info.json b/src/json/schema/container/info.json +index 53d8a91..3bf1033 100644 +--- a/src/json/schema/container/info.json ++++ b/src/json/schema/container/info.json +@@ -79,6 +79,12 @@ + }, + "timestamp": { + "type": "uint64" ++ }, ++ "swap_used": { ++ "type": "uint64" ++ }, ++ "swap_limit": { ++ "type": "uint64" + } + } + } +diff --git a/src/json/schema/shim/client/runtime-stats.json b/src/json/schema/shim/client/runtime-stats.json +index 35cfe64..9d92a81 100644 +--- a/src/json/schema/shim/client/runtime-stats.json ++++ b/src/json/schema/shim/client/runtime-stats.json +@@ -63,6 +63,17 @@ + "$ref": "../../defs.json#/definitions/uint64" + } + } ++ }, ++ "swap": { ++ "type": "object", ++ "properties": { ++ "usage": { ++ "$ref": "../../defs.json#/definitions/uint64" ++ }, ++ "limit": { ++ "$ref": "../../defs.json#/definitions/uint64" ++ } ++ } + } + } + }, +-- +2.33.0 + diff --git a/lcr.spec b/lcr.spec index f98ca21..a7b3720 100644 --- a/lcr.spec +++ b/lcr.spec @@ -1,5 +1,5 @@ %global _version 2.1.4 -%global _release 1 +%global _release 2 %global _inner_name isula_libutils %global enable_lxc 1 @@ -13,6 +13,10 @@ Group: Applications/System License: LGPLv2.1+ BuildRoot: %{_tmppath}/lcr-%{version} +Patch0001: 0001-add-systemd-cgroup-field-in-isulad-and-shim-config.patch +Patch0002: 0002-Add-http-parser-as-third-party-component.patch +Patch0003: 0003-add-swap-usage-fields-in-shim-stats-and-container-in.patch + %define lxcver_lower 4.0.3-2022102400 %define lxcver_upper 5.0.3 @@ -127,6 +131,12 @@ rm -rf %{buildroot} %{_includedir}/lcr/utils_compile.h %changelog +* Tue Mar 19 2024 jikai - 2.1.4-2 +- Type:enhancement +- CVE:NA +- SUG:NA +- DESC:sync from upstream + * Tue Jan 30 2024 jikai - 2.1.4-1 - Type:enhancement - CVE:NA