diff --git a/CHANGELOG.md b/CHANGELOG.md index ecac7dab539..ef690dd2ab0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,8 @@ release. -24.18.0
+24.18.1
+24.18.0
24.17.0
24.16.0
24.15.0
diff --git a/deps/llhttp/.gitignore b/deps/llhttp/.gitignore deleted file mode 100644 index 98438a2cd32..00000000000 --- a/deps/llhttp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libllhttp.pc diff --git a/deps/llhttp/CMakeLists.txt b/deps/llhttp/CMakeLists.txt index 856f9fd09a9..e4629bf1cd2 100644 --- a/deps/llhttp/CMakeLists.txt +++ b/deps/llhttp/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.25.0) cmake_policy(SET CMP0069 NEW) -project(llhttp VERSION 9.4.2) +project(llhttp VERSION 9.4.3) include(GNUInstallDirs) include(CMakePackageConfigHelpers) diff --git a/deps/llhttp/include/llhttp.h b/deps/llhttp/include/llhttp.h index 5670da52296..da24ef156ef 100644 --- a/deps/llhttp/include/llhttp.h +++ b/deps/llhttp/include/llhttp.h @@ -4,7 +4,7 @@ #define LLHTTP_VERSION_MAJOR 9 #define LLHTTP_VERSION_MINOR 4 -#define LLHTTP_VERSION_PATCH 2 +#define LLHTTP_VERSION_PATCH 3 #ifndef INCLUDE_LLHTTP_ITSELF_H_ #define INCLUDE_LLHTTP_ITSELF_H_ diff --git a/deps/llhttp/src/llhttp.c b/deps/llhttp/src/llhttp.c index cbbb36cd955..73545770337 100644 --- a/deps/llhttp/src/llhttp.c +++ b/deps/llhttp/src/llhttp.c @@ -402,7 +402,7 @@ enum llparse_state_e { s_n_llhttp__internal__n_invoke_test_lenient_flags_17, s_n_llhttp__internal__n_header_value_lenient, s_n_llhttp__internal__n_header_value_relaxed, - s_n_llhttp__internal__n_error_54, + s_n_llhttp__internal__n_error_55, s_n_llhttp__internal__n_header_value_otherwise, s_n_llhttp__internal__n_header_value_connection_token, s_n_llhttp__internal__n_header_value_connection_ws, @@ -410,12 +410,12 @@ enum llparse_state_e { s_n_llhttp__internal__n_header_value_connection_2, s_n_llhttp__internal__n_header_value_connection_3, s_n_llhttp__internal__n_header_value_connection, - s_n_llhttp__internal__n_error_56, s_n_llhttp__internal__n_error_57, + s_n_llhttp__internal__n_error_58, s_n_llhttp__internal__n_header_value_content_length_ws, s_n_llhttp__internal__n_header_value_content_length, + s_n_llhttp__internal__n_error_60, s_n_llhttp__internal__n_error_59, - s_n_llhttp__internal__n_error_58, s_n_llhttp__internal__n_header_value_te_token_ows, s_n_llhttp__internal__n_header_value, s_n_llhttp__internal__n_header_value_te_token, @@ -448,18 +448,18 @@ enum llparse_state_e { s_n_llhttp__internal__n_req_http_complete, s_n_llhttp__internal__n_invoke_load_method_1, s_n_llhttp__internal__n_invoke_llhttp__on_version_complete, - s_n_llhttp__internal__n_error_67, - s_n_llhttp__internal__n_error_74, - s_n_llhttp__internal__n_req_http_minor, + s_n_llhttp__internal__n_error_68, s_n_llhttp__internal__n_error_75, - s_n_llhttp__internal__n_req_http_dot, + s_n_llhttp__internal__n_req_http_minor, s_n_llhttp__internal__n_error_76, + s_n_llhttp__internal__n_req_http_dot, + s_n_llhttp__internal__n_error_77, s_n_llhttp__internal__n_req_http_major, s_n_llhttp__internal__n_span_start_llhttp__on_version, s_n_llhttp__internal__n_req_after_protocol, s_n_llhttp__internal__n_invoke_load_method, s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete, - s_n_llhttp__internal__n_error_82, + s_n_llhttp__internal__n_error_83, s_n_llhttp__internal__n_req_after_http_start_1, s_n_llhttp__internal__n_invoke_load_method_2, s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1, @@ -576,17 +576,17 @@ enum llparse_state_e { s_n_llhttp__internal__n_res_status_code_digit_1, s_n_llhttp__internal__n_res_after_version, s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1, - s_n_llhttp__internal__n_error_93, - s_n_llhttp__internal__n_error_107, - s_n_llhttp__internal__n_res_http_minor, + s_n_llhttp__internal__n_error_94, s_n_llhttp__internal__n_error_108, - s_n_llhttp__internal__n_res_http_dot, + s_n_llhttp__internal__n_res_http_minor, s_n_llhttp__internal__n_error_109, + s_n_llhttp__internal__n_res_http_dot, + s_n_llhttp__internal__n_error_110, s_n_llhttp__internal__n_res_http_major, s_n_llhttp__internal__n_span_start_llhttp__on_version_1, s_n_llhttp__internal__n_res_after_protocol, s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3, - s_n_llhttp__internal__n_error_115, + s_n_llhttp__internal__n_error_116, s_n_llhttp__internal__n_res_after_start_1, s_n_llhttp__internal__n_res_after_start_2, s_n_llhttp__internal__n_res_after_start_3, @@ -2227,7 +2227,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_header_value_lws; } default: { - goto s_n_llhttp__internal__n_error_53; + goto s_n_llhttp__internal__n_error_54; } } UNREACHABLE; @@ -2238,7 +2238,7 @@ static llparse_state_t llhttp__internal__run( case 1: goto s_n_llhttp__internal__n_header_value_almost_done; default: - goto s_n_llhttp__internal__n_error_51; + goto s_n_llhttp__internal__n_error_52; } UNREACHABLE; } @@ -2400,8 +2400,8 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - case s_n_llhttp__internal__n_error_54: - s_n_llhttp__internal__n_error_54: { + case s_n_llhttp__internal__n_error_55: + s_n_llhttp__internal__n_error_55: { state->error = 0xa; state->reason = "Invalid header value char"; state->error_pos = (const char*) p; @@ -2596,8 +2596,8 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - case s_n_llhttp__internal__n_error_56: - s_n_llhttp__internal__n_error_56: { + case s_n_llhttp__internal__n_error_57: + s_n_llhttp__internal__n_error_57: { state->error = 0xb; state->reason = "Content-Length overflow"; state->error_pos = (const char*) p; @@ -2605,8 +2605,8 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - case s_n_llhttp__internal__n_error_57: - s_n_llhttp__internal__n_error_57: { + case s_n_llhttp__internal__n_error_58: + s_n_llhttp__internal__n_error_58: { state->error = 0xb; state->reason = "Invalid character in Content-Length"; state->error_pos = (const char*) p; @@ -2702,8 +2702,8 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - case s_n_llhttp__internal__n_error_59: - s_n_llhttp__internal__n_error_59: { + case s_n_llhttp__internal__n_error_60: + s_n_llhttp__internal__n_error_60: { state->error = 0xf; state->reason = "Invalid `Transfer-Encoding` header value"; state->error_pos = (const char*) p; @@ -2711,8 +2711,8 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - case s_n_llhttp__internal__n_error_58: - s_n_llhttp__internal__n_error_58: { + case s_n_llhttp__internal__n_error_59: + s_n_llhttp__internal__n_error_59: { state->error = 0xf; state->reason = "Invalid `Transfer-Encoding` header value"; state->error_pos = (const char*) p; @@ -3031,7 +3031,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2; } default: { - goto s_n_llhttp__internal__n_error_62; + goto s_n_llhttp__internal__n_error_63; } } UNREACHABLE; @@ -3415,7 +3415,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_to_http_09; } default: { - goto s_n_llhttp__internal__n_error_63; + goto s_n_llhttp__internal__n_error_64; } } UNREACHABLE; @@ -3439,7 +3439,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_skip_lf_to_http09_1; } default: { - goto s_n_llhttp__internal__n_error_63; + goto s_n_llhttp__internal__n_error_64; } } UNREACHABLE; @@ -3456,13 +3456,13 @@ static llparse_state_t llhttp__internal__run( switch (match_seq.status) { case kMatchComplete: { p++; - goto s_n_llhttp__internal__n_error_72; + goto s_n_llhttp__internal__n_error_73; } case kMatchPause: { return s_n_llhttp__internal__n_req_pri_upgrade; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_73; + goto s_n_llhttp__internal__n_error_74; } } UNREACHABLE; @@ -3498,7 +3498,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_req_http_complete_crlf; } default: { - goto s_n_llhttp__internal__n_error_71; + goto s_n_llhttp__internal__n_error_72; } } UNREACHABLE; @@ -3521,12 +3521,12 @@ static llparse_state_t llhttp__internal__run( case 21: goto s_n_llhttp__internal__n_pause_21; default: - goto s_n_llhttp__internal__n_error_68; + goto s_n_llhttp__internal__n_error_69; } UNREACHABLE; } - case s_n_llhttp__internal__n_error_67: - s_n_llhttp__internal__n_error_67: { + case s_n_llhttp__internal__n_error_68: + s_n_llhttp__internal__n_error_68: { state->error = 0x9; state->reason = "Invalid HTTP version"; state->error_pos = (const char*) p; @@ -3534,8 +3534,8 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - case s_n_llhttp__internal__n_error_74: - s_n_llhttp__internal__n_error_74: { + case s_n_llhttp__internal__n_error_75: + s_n_llhttp__internal__n_error_75: { state->error = 0x9; state->reason = "Invalid minor version"; state->error_pos = (const char*) p; @@ -3605,8 +3605,8 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - case s_n_llhttp__internal__n_error_75: - s_n_llhttp__internal__n_error_75: { + case s_n_llhttp__internal__n_error_76: + s_n_llhttp__internal__n_error_76: { state->error = 0x9; state->reason = "Expected dot"; state->error_pos = (const char*) p; @@ -3630,8 +3630,8 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - case s_n_llhttp__internal__n_error_76: - s_n_llhttp__internal__n_error_76: { + case s_n_llhttp__internal__n_error_77: + s_n_llhttp__internal__n_error_77: { state->error = 0x9; state->reason = "Invalid major version"; state->error_pos = (const char*) p; @@ -3722,7 +3722,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_span_start_llhttp__on_version; } default: { - goto s_n_llhttp__internal__n_error_77; + goto s_n_llhttp__internal__n_error_78; } } UNREACHABLE; @@ -3803,7 +3803,7 @@ static llparse_state_t llhttp__internal__run( case 46: goto s_n_llhttp__internal__n_req_after_protocol; default: - goto s_n_llhttp__internal__n_error_66; + goto s_n_llhttp__internal__n_error_67; } UNREACHABLE; } @@ -3815,12 +3815,12 @@ static llparse_state_t llhttp__internal__run( case 21: goto s_n_llhttp__internal__n_pause_22; default: - goto s_n_llhttp__internal__n_error_65; + goto s_n_llhttp__internal__n_error_66; } UNREACHABLE; } - case s_n_llhttp__internal__n_error_82: - s_n_llhttp__internal__n_error_82: { + case s_n_llhttp__internal__n_error_83: + s_n_llhttp__internal__n_error_83: { state->error = 0x8; state->reason = "Expected HTTP/, RTSP/ or ICE/"; state->error_pos = (const char*) p; @@ -3857,7 +3857,7 @@ static llparse_state_t llhttp__internal__run( case 33: goto s_n_llhttp__internal__n_req_after_protocol; default: - goto s_n_llhttp__internal__n_error_79; + goto s_n_llhttp__internal__n_error_80; } UNREACHABLE; } @@ -3869,7 +3869,7 @@ static llparse_state_t llhttp__internal__run( case 21: goto s_n_llhttp__internal__n_pause_23; default: - goto s_n_llhttp__internal__n_error_78; + goto s_n_llhttp__internal__n_error_79; } UNREACHABLE; } @@ -3928,7 +3928,7 @@ static llparse_state_t llhttp__internal__run( case 45: goto s_n_llhttp__internal__n_req_after_protocol; default: - goto s_n_llhttp__internal__n_error_81; + goto s_n_llhttp__internal__n_error_82; } UNREACHABLE; } @@ -3940,7 +3940,7 @@ static llparse_state_t llhttp__internal__run( case 21: goto s_n_llhttp__internal__n_pause_24; default: - goto s_n_llhttp__internal__n_error_80; + goto s_n_llhttp__internal__n_error_81; } UNREACHABLE; } @@ -4100,7 +4100,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_fragment; } default: { - goto s_n_llhttp__internal__n_error_83; + goto s_n_llhttp__internal__n_error_84; } } UNREACHABLE; @@ -4159,7 +4159,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_span_end_stub_query_3; } default: { - goto s_n_llhttp__internal__n_error_84; + goto s_n_llhttp__internal__n_error_85; } } UNREACHABLE; @@ -4196,7 +4196,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_query; } default: { - goto s_n_llhttp__internal__n_error_85; + goto s_n_llhttp__internal__n_error_86; } } UNREACHABLE; @@ -4316,10 +4316,10 @@ static llparse_state_t llhttp__internal__run( } case 8: { p++; - goto s_n_llhttp__internal__n_error_86; + goto s_n_llhttp__internal__n_error_87; } default: { - goto s_n_llhttp__internal__n_error_87; + goto s_n_llhttp__internal__n_error_88; } } UNREACHABLE; @@ -4377,7 +4377,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_server_with_at; } default: { - goto s_n_llhttp__internal__n_error_88; + goto s_n_llhttp__internal__n_error_89; } } UNREACHABLE; @@ -4393,7 +4393,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_server; } default: { - goto s_n_llhttp__internal__n_error_89; + goto s_n_llhttp__internal__n_error_90; } } UNREACHABLE; @@ -4429,7 +4429,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_schema_delim_1; } default: { - goto s_n_llhttp__internal__n_error_89; + goto s_n_llhttp__internal__n_error_90; } } UNREACHABLE; @@ -4479,7 +4479,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_schema; } default: { - goto s_n_llhttp__internal__n_error_90; + goto s_n_llhttp__internal__n_error_91; } } UNREACHABLE; @@ -4519,7 +4519,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_schema; } default: { - goto s_n_llhttp__internal__n_error_91; + goto s_n_llhttp__internal__n_error_92; } } UNREACHABLE; @@ -4611,7 +4611,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_req_spaces_before_url; } default: { - goto s_n_llhttp__internal__n_error_92; + goto s_n_llhttp__internal__n_error_93; } } UNREACHABLE; @@ -4624,7 +4624,7 @@ static llparse_state_t llhttp__internal__run( case 21: goto s_n_llhttp__internal__n_pause_29; default: - goto s_n_llhttp__internal__n_error_111; + goto s_n_llhttp__internal__n_error_112; } UNREACHABLE; } @@ -4640,7 +4640,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4664,7 +4664,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_3; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4684,7 +4684,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_3; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4708,7 +4708,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_4; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4732,7 +4732,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_6; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4756,7 +4756,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_8; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4773,7 +4773,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4793,7 +4793,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_9; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4813,7 +4813,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_7; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4837,7 +4837,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_12; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4861,7 +4861,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_13; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4881,7 +4881,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_13; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4897,7 +4897,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_11; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4921,7 +4921,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_14; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4945,7 +4945,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_17; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -4985,7 +4985,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_15; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5009,7 +5009,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_18; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5033,7 +5033,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_20; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5057,7 +5057,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_21; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5077,7 +5077,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_21; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5101,7 +5101,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_23; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5125,7 +5125,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_24; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5149,7 +5149,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_26; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5173,7 +5173,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_28; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5190,7 +5190,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5210,7 +5210,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_29; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5230,7 +5230,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_27; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5254,7 +5254,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_30; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5282,7 +5282,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_30; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5306,7 +5306,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_31; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5330,7 +5330,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_32; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5354,7 +5354,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_35; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5378,7 +5378,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_36; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5398,7 +5398,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_36; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5422,7 +5422,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_37; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5446,7 +5446,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_38; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5470,7 +5470,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_42; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5494,7 +5494,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_43; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5514,7 +5514,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_43; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5530,7 +5530,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_41; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5551,7 +5551,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_40; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5575,7 +5575,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_45; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5596,7 +5596,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5628,7 +5628,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_44; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5652,7 +5652,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_46; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5676,7 +5676,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_49; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5700,7 +5700,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_50; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5724,7 +5724,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_51; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5748,7 +5748,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_52; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5776,7 +5776,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_52; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5792,7 +5792,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_48; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5816,7 +5816,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_55; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5833,7 +5833,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5857,7 +5857,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_58; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5877,7 +5877,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_58; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5897,7 +5897,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_56; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5921,7 +5921,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_59; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5945,7 +5945,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_60; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5969,7 +5969,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_60; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -5993,7 +5993,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_62; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -6017,7 +6017,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_63; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -6037,7 +6037,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_63; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -6061,7 +6061,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_66; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -6085,7 +6085,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_68; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -6109,7 +6109,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_69; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -6129,7 +6129,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_69; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -6153,7 +6153,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_after_start_req_70; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -6177,7 +6177,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_70; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -6193,7 +6193,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_65; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -6273,7 +6273,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_after_start_req_64; } default: { - goto s_n_llhttp__internal__n_error_112; + goto s_n_llhttp__internal__n_error_113; } } UNREACHABLE; @@ -6310,7 +6310,7 @@ static llparse_state_t llhttp__internal__run( case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; default: - goto s_n_llhttp__internal__n_error_98; + goto s_n_llhttp__internal__n_error_99; } UNREACHABLE; } @@ -6362,7 +6362,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_span_start_llhttp__on_status; } default: { - goto s_n_llhttp__internal__n_error_99; + goto s_n_llhttp__internal__n_error_100; } } UNREACHABLE; @@ -6424,7 +6424,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; } default: { - goto s_n_llhttp__internal__n_error_101; + goto s_n_llhttp__internal__n_error_102; } } UNREACHABLE; @@ -6486,7 +6486,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; } default: { - goto s_n_llhttp__internal__n_error_103; + goto s_n_llhttp__internal__n_error_104; } } UNREACHABLE; @@ -6548,7 +6548,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } default: { - goto s_n_llhttp__internal__n_error_105; + goto s_n_llhttp__internal__n_error_106; } } UNREACHABLE; @@ -6564,7 +6564,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_invoke_update_status_code; } default: { - goto s_n_llhttp__internal__n_error_106; + goto s_n_llhttp__internal__n_error_107; } } UNREACHABLE; @@ -6577,12 +6577,12 @@ static llparse_state_t llhttp__internal__run( case 21: goto s_n_llhttp__internal__n_pause_28; default: - goto s_n_llhttp__internal__n_error_94; + goto s_n_llhttp__internal__n_error_95; } UNREACHABLE; } - case s_n_llhttp__internal__n_error_93: - s_n_llhttp__internal__n_error_93: { + case s_n_llhttp__internal__n_error_94: + s_n_llhttp__internal__n_error_94: { state->error = 0x9; state->reason = "Invalid HTTP version"; state->error_pos = (const char*) p; @@ -6590,8 +6590,8 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - case s_n_llhttp__internal__n_error_107: - s_n_llhttp__internal__n_error_107: { + case s_n_llhttp__internal__n_error_108: + s_n_llhttp__internal__n_error_108: { state->error = 0x9; state->reason = "Invalid minor version"; state->error_pos = (const char*) p; @@ -6661,8 +6661,8 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - case s_n_llhttp__internal__n_error_108: - s_n_llhttp__internal__n_error_108: { + case s_n_llhttp__internal__n_error_109: + s_n_llhttp__internal__n_error_109: { state->error = 0x9; state->reason = "Expected dot"; state->error_pos = (const char*) p; @@ -6686,8 +6686,8 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - case s_n_llhttp__internal__n_error_109: - s_n_llhttp__internal__n_error_109: { + case s_n_llhttp__internal__n_error_110: + s_n_llhttp__internal__n_error_110: { state->error = 0x9; state->reason = "Invalid major version"; state->error_pos = (const char*) p; @@ -6778,7 +6778,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1; } default: { - goto s_n_llhttp__internal__n_error_114; + goto s_n_llhttp__internal__n_error_115; } } UNREACHABLE; @@ -6791,12 +6791,12 @@ static llparse_state_t llhttp__internal__run( case 21: goto s_n_llhttp__internal__n_pause_30; default: - goto s_n_llhttp__internal__n_error_113; + goto s_n_llhttp__internal__n_error_114; } UNREACHABLE; } - case s_n_llhttp__internal__n_error_115: - s_n_llhttp__internal__n_error_115: { + case s_n_llhttp__internal__n_error_116: + s_n_llhttp__internal__n_error_116: { state->error = 0x8; state->reason = "Expected HTTP/, RTSP/ or ICE/"; state->error_pos = (const char*) p; @@ -6938,7 +6938,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_req_or_res_method_2; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_110; + goto s_n_llhttp__internal__n_error_111; } } UNREACHABLE; @@ -6969,7 +6969,7 @@ static llparse_state_t llhttp__internal__run( return s_n_llhttp__internal__n_req_or_res_method_3; } case kMatchMismatch: { - goto s_n_llhttp__internal__n_error_110; + goto s_n_llhttp__internal__n_error_111; } } UNREACHABLE; @@ -6989,7 +6989,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_req_or_res_method_3; } default: { - goto s_n_llhttp__internal__n_error_110; + goto s_n_llhttp__internal__n_error_111; } } UNREACHABLE; @@ -7005,7 +7005,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_req_or_res_method_1; } default: { - goto s_n_llhttp__internal__n_error_110; + goto s_n_llhttp__internal__n_error_111; } } UNREACHABLE; @@ -8083,7 +8083,7 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - s_n_llhttp__internal__n_error_60: { + s_n_llhttp__internal__n_error_61: { state->error = 0xb; state->reason = "Content-Length can't be present with Transfer-Encoding"; state->error_pos = (const char*) p; @@ -8116,6 +8116,14 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } + s_n_llhttp__internal__n_error_50: { + state->error = 0xf; + state->reason = "Empty Transfer-Encoding"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } s_n_llhttp__internal__n_pause_18: { state->error = 0x15; state->reason = "on_header_value_complete pause"; @@ -8202,6 +8210,8 @@ static llparse_state_t llhttp__internal__run( switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 2: goto s_n_llhttp__internal__n_error_49; + case 3: + goto s_n_llhttp__internal__n_error_50; default: goto s_n_llhttp__internal__n_invoke_load_header_state_2; } @@ -8224,7 +8234,7 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - s_n_llhttp__internal__n_error_50: { + s_n_llhttp__internal__n_error_51: { state->error = 0x2; state->reason = "Expected LF after CR"; state->error_pos = (const char*) p; @@ -8237,7 +8247,7 @@ static llparse_state_t llhttp__internal__run( case 1: goto s_n_llhttp__internal__n_header_value_discard_lws; default: - goto s_n_llhttp__internal__n_error_50; + goto s_n_llhttp__internal__n_error_51; } UNREACHABLE; } @@ -8257,7 +8267,7 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - s_n_llhttp__internal__n_error_52: { + s_n_llhttp__internal__n_error_53: { state->error = 0xa; state->reason = "Unexpected whitespace after header value"; state->error_pos = (const char*) p; @@ -8270,7 +8280,7 @@ static llparse_state_t llhttp__internal__run( case 1: goto s_n_llhttp__internal__n_invoke_load_header_state_4; default: - goto s_n_llhttp__internal__n_error_52; + goto s_n_llhttp__internal__n_error_53; } UNREACHABLE; } @@ -8324,7 +8334,7 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - s_n_llhttp__internal__n_error_53: { + s_n_llhttp__internal__n_error_54: { state->error = 0x3; state->reason = "Missing expected LF after header value"; state->error_pos = (const char*) p; @@ -8332,7 +8342,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_51: { + s_n_llhttp__internal__n_error_52: { state->error = 0x19; state->reason = "Missing expected CR after header value"; state->error_pos = (const char*) p; @@ -8416,10 +8426,10 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_54; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_55; return s_error; } - goto s_n_llhttp__internal__n_error_54; + goto s_n_llhttp__internal__n_error_55; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_20: { @@ -8528,10 +8538,10 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_56; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_57; return s_error; } - goto s_n_llhttp__internal__n_error_56; + goto s_n_llhttp__internal__n_error_57; UNREACHABLE; } s_n_llhttp__internal__n_invoke_mul_add_content_length_1: { @@ -8560,13 +8570,13 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_57; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_58; return s_error; } - goto s_n_llhttp__internal__n_error_57; + goto s_n_llhttp__internal__n_error_58; UNREACHABLE; } - s_n_llhttp__internal__n_error_55: { + s_n_llhttp__internal__n_error_56: { state->error = 0x4; state->reason = "Duplicate Content-Length"; state->error_pos = (const char*) p; @@ -8579,7 +8589,7 @@ static llparse_state_t llhttp__internal__run( case 0: goto s_n_llhttp__internal__n_header_value_content_length; default: - goto s_n_llhttp__internal__n_error_55; + goto s_n_llhttp__internal__n_error_56; } UNREACHABLE; } @@ -8593,11 +8603,11 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_59; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_60; return s_error; } p++; - goto s_n_llhttp__internal__n_error_59; + goto s_n_llhttp__internal__n_error_60; UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_8: { @@ -8617,11 +8627,11 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_58; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_59; return s_error; } p++; - goto s_n_llhttp__internal__n_error_58; + goto s_n_llhttp__internal__n_error_59; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_21: { @@ -8722,7 +8732,7 @@ static llparse_state_t llhttp__internal__run( s_n_llhttp__internal__n_invoke_test_lenient_flags_23: { switch (llhttp__internal__c_test_lenient_flags_23(state, p, endp)) { case 0: - goto s_n_llhttp__internal__n_error_60; + goto s_n_llhttp__internal__n_error_61; default: goto s_n_llhttp__internal__n_header_value_discard_ws; } @@ -8737,7 +8747,7 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - s_n_llhttp__internal__n_error_61: { + s_n_llhttp__internal__n_error_62: { state->error = 0xf; state->reason = "Transfer-Encoding can't be present with Content-Length"; state->error_pos = (const char*) p; @@ -8748,7 +8758,7 @@ static llparse_state_t llhttp__internal__run( s_n_llhttp__internal__n_invoke_test_lenient_flags_24: { switch (llhttp__internal__c_test_lenient_flags_23(state, p, endp)) { case 0: - goto s_n_llhttp__internal__n_error_61; + goto s_n_llhttp__internal__n_error_62; default: goto s_n_llhttp__internal__n_header_value_discard_ws; } @@ -8813,7 +8823,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; UNREACHABLE; } - s_n_llhttp__internal__n_error_62: { + s_n_llhttp__internal__n_error_63: { state->error = 0xa; state->reason = "Invalid header token"; state->error_pos = (const char*) p; @@ -8916,7 +8926,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_skip_to_http09; UNREACHABLE; } - s_n_llhttp__internal__n_error_63: { + s_n_llhttp__internal__n_error_64: { state->error = 0x7; state->reason = "Expected CRLF"; state->error_pos = (const char*) p; @@ -8940,7 +8950,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_skip_lf_to_http09; UNREACHABLE; } - s_n_llhttp__internal__n_error_72: { + s_n_llhttp__internal__n_error_73: { state->error = 0x17; state->reason = "Pause on PRI/Upgrade"; state->error_pos = (const char*) p; @@ -8948,7 +8958,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_73: { + s_n_llhttp__internal__n_error_74: { state->error = 0x9; state->reason = "Expected HTTP/2 Connection Preface"; state->error_pos = (const char*) p; @@ -8956,7 +8966,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_70: { + s_n_llhttp__internal__n_error_71: { state->error = 0x2; state->reason = "Expected CRLF after version"; state->error_pos = (const char*) p; @@ -8969,11 +8979,11 @@ static llparse_state_t llhttp__internal__run( case 1: goto s_n_llhttp__internal__n_headers_start; default: - goto s_n_llhttp__internal__n_error_70; + goto s_n_llhttp__internal__n_error_71; } UNREACHABLE; } - s_n_llhttp__internal__n_error_69: { + s_n_llhttp__internal__n_error_70: { state->error = 0x9; state->reason = "Expected CRLF after version"; state->error_pos = (const char*) p; @@ -8986,11 +8996,11 @@ static llparse_state_t llhttp__internal__run( case 1: goto s_n_llhttp__internal__n_req_http_complete_crlf; default: - goto s_n_llhttp__internal__n_error_69; + goto s_n_llhttp__internal__n_error_70; } UNREACHABLE; } - s_n_llhttp__internal__n_error_71: { + s_n_llhttp__internal__n_error_72: { state->error = 0x9; state->reason = "Expected CRLF after version"; state->error_pos = (const char*) p; @@ -9006,7 +9016,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_68: { + s_n_llhttp__internal__n_error_69: { state->error = 0x21; state->reason = "`on_version_complete` callback error"; state->error_pos = (const char*) p; @@ -9040,10 +9050,10 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_67; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_68; return s_error; } - goto s_n_llhttp__internal__n_error_67; + goto s_n_llhttp__internal__n_error_68; UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_http_minor: { @@ -9114,10 +9124,10 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_74; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_75; return s_error; } - goto s_n_llhttp__internal__n_error_74; + goto s_n_llhttp__internal__n_error_75; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version_3: { @@ -9130,10 +9140,10 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_75; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_76; return s_error; } - goto s_n_llhttp__internal__n_error_75; + goto s_n_llhttp__internal__n_error_76; UNREACHABLE; } s_n_llhttp__internal__n_invoke_store_http_major: { @@ -9153,13 +9163,13 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_76; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_77; return s_error; } - goto s_n_llhttp__internal__n_error_76; + goto s_n_llhttp__internal__n_error_77; UNREACHABLE; } - s_n_llhttp__internal__n_error_77: { + s_n_llhttp__internal__n_error_78: { state->error = 0x8; state->reason = "Expected HTTP/, RTSP/ or ICE/"; state->error_pos = (const char*) p; @@ -9167,7 +9177,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_66: { + s_n_llhttp__internal__n_error_67: { state->error = 0x8; state->reason = "Invalid method for HTTP/x.x request"; state->error_pos = (const char*) p; @@ -9183,7 +9193,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_65: { + s_n_llhttp__internal__n_error_66: { state->error = 0x26; state->reason = "`on_protocol_complete` callback error"; state->error_pos = (const char*) p; @@ -9217,13 +9227,13 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_82; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_83; return s_error; } - goto s_n_llhttp__internal__n_error_82; + goto s_n_llhttp__internal__n_error_83; UNREACHABLE; } - s_n_llhttp__internal__n_error_79: { + s_n_llhttp__internal__n_error_80: { state->error = 0x8; state->reason = "Expected SOURCE method for ICE/x.x request"; state->error_pos = (const char*) p; @@ -9239,7 +9249,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_78: { + s_n_llhttp__internal__n_error_79: { state->error = 0x26; state->reason = "`on_protocol_complete` callback error"; state->error_pos = (const char*) p; @@ -9263,7 +9273,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1; UNREACHABLE; } - s_n_llhttp__internal__n_error_81: { + s_n_llhttp__internal__n_error_82: { state->error = 0x8; state->reason = "Invalid method for RTSP/x.x request"; state->error_pos = (const char*) p; @@ -9279,7 +9289,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_80: { + s_n_llhttp__internal__n_error_81: { state->error = 0x26; state->reason = "`on_protocol_complete` callback error"; state->error_pos = (const char*) p; @@ -9311,7 +9321,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_64: { + s_n_llhttp__internal__n_error_65: { state->error = 0x1a; state->reason = "`on_url_complete` callback error"; state->error_pos = (const char*) p; @@ -9326,7 +9336,7 @@ static llparse_state_t llhttp__internal__run( case 21: goto s_n_llhttp__internal__n_pause_25; default: - goto s_n_llhttp__internal__n_error_64; + goto s_n_llhttp__internal__n_error_65; } UNREACHABLE; } @@ -9394,7 +9404,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_skip_to_http; UNREACHABLE; } - s_n_llhttp__internal__n_error_83: { + s_n_llhttp__internal__n_error_84: { state->error = 0x7; state->reason = "Invalid char in url fragment start"; state->error_pos = (const char*) p; @@ -9450,7 +9460,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_skip_to_http; UNREACHABLE; } - s_n_llhttp__internal__n_error_84: { + s_n_llhttp__internal__n_error_85: { state->error = 0x7; state->reason = "Invalid char in url query"; state->error_pos = (const char*) p; @@ -9458,7 +9468,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_85: { + s_n_llhttp__internal__n_error_86: { state->error = 0x7; state->reason = "Invalid char in url path"; state->error_pos = (const char*) p; @@ -9562,7 +9572,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_url_skip_to_http; UNREACHABLE; } - s_n_llhttp__internal__n_error_86: { + s_n_llhttp__internal__n_error_87: { state->error = 0x7; state->reason = "Double @ in url"; state->error_pos = (const char*) p; @@ -9570,7 +9580,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_87: { + s_n_llhttp__internal__n_error_88: { state->error = 0x7; state->reason = "Unexpected char in url server"; state->error_pos = (const char*) p; @@ -9578,7 +9588,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_88: { + s_n_llhttp__internal__n_error_89: { state->error = 0x7; state->reason = "Unexpected char in url server"; state->error_pos = (const char*) p; @@ -9586,7 +9596,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_89: { + s_n_llhttp__internal__n_error_90: { state->error = 0x7; state->reason = "Unexpected char in url schema"; state->error_pos = (const char*) p; @@ -9594,7 +9604,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_90: { + s_n_llhttp__internal__n_error_91: { state->error = 0x7; state->reason = "Unexpected char in url schema"; state->error_pos = (const char*) p; @@ -9602,7 +9612,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_91: { + s_n_llhttp__internal__n_error_92: { state->error = 0x7; state->reason = "Unexpected start char in url"; state->error_pos = (const char*) p; @@ -9619,7 +9629,7 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - s_n_llhttp__internal__n_error_92: { + s_n_llhttp__internal__n_error_93: { state->error = 0x6; state->reason = "Expected space after method"; state->error_pos = (const char*) p; @@ -9635,7 +9645,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_111: { + s_n_llhttp__internal__n_error_112: { state->error = 0x20; state->reason = "`on_method_complete` callback error"; state->error_pos = (const char*) p; @@ -9666,7 +9676,7 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - s_n_llhttp__internal__n_error_112: { + s_n_llhttp__internal__n_error_113: { state->error = 0x6; state->reason = "Invalid method encountered"; state->error_pos = (const char*) p; @@ -9674,7 +9684,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_104: { + s_n_llhttp__internal__n_error_105: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; @@ -9682,7 +9692,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_102: { + s_n_llhttp__internal__n_error_103: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; @@ -9690,7 +9700,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_100: { + s_n_llhttp__internal__n_error_101: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; @@ -9706,7 +9716,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_96: { + s_n_llhttp__internal__n_error_97: { state->error = 0x1b; state->reason = "`on_status_complete` callback error"; state->error_pos = (const char*) p; @@ -9721,11 +9731,11 @@ static llparse_state_t llhttp__internal__run( case 21: goto s_n_llhttp__internal__n_pause_27; default: - goto s_n_llhttp__internal__n_error_96; + goto s_n_llhttp__internal__n_error_97; } UNREACHABLE; } - s_n_llhttp__internal__n_error_95: { + s_n_llhttp__internal__n_error_96: { state->error = 0xd; state->reason = "Invalid response status"; state->error_pos = (const char*) p; @@ -9738,11 +9748,11 @@ static llparse_state_t llhttp__internal__run( case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; default: - goto s_n_llhttp__internal__n_error_95; + goto s_n_llhttp__internal__n_error_96; } UNREACHABLE; } - s_n_llhttp__internal__n_error_97: { + s_n_llhttp__internal__n_error_98: { state->error = 0x2; state->reason = "Expected LF after CR"; state->error_pos = (const char*) p; @@ -9755,11 +9765,11 @@ static llparse_state_t llhttp__internal__run( case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; default: - goto s_n_llhttp__internal__n_error_97; + goto s_n_llhttp__internal__n_error_98; } UNREACHABLE; } - s_n_llhttp__internal__n_error_98: { + s_n_llhttp__internal__n_error_99: { state->error = 0x19; state->reason = "Missing expected CR after response line"; state->error_pos = (const char*) p; @@ -9801,7 +9811,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_res_line_almost_done; UNREACHABLE; } - s_n_llhttp__internal__n_error_99: { + s_n_llhttp__internal__n_error_100: { state->error = 0xd; state->reason = "Invalid response status"; state->error_pos = (const char*) p; @@ -9812,13 +9822,13 @@ static llparse_state_t llhttp__internal__run( s_n_llhttp__internal__n_invoke_mul_add_status_code_2: { switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { case 1: - goto s_n_llhttp__internal__n_error_100; + goto s_n_llhttp__internal__n_error_101; default: goto s_n_llhttp__internal__n_res_status_code_otherwise; } UNREACHABLE; } - s_n_llhttp__internal__n_error_101: { + s_n_llhttp__internal__n_error_102: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; @@ -9829,13 +9839,13 @@ static llparse_state_t llhttp__internal__run( s_n_llhttp__internal__n_invoke_mul_add_status_code_1: { switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { case 1: - goto s_n_llhttp__internal__n_error_102; + goto s_n_llhttp__internal__n_error_103; default: goto s_n_llhttp__internal__n_res_status_code_digit_3; } UNREACHABLE; } - s_n_llhttp__internal__n_error_103: { + s_n_llhttp__internal__n_error_104: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; @@ -9846,13 +9856,13 @@ static llparse_state_t llhttp__internal__run( s_n_llhttp__internal__n_invoke_mul_add_status_code: { switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { case 1: - goto s_n_llhttp__internal__n_error_104; + goto s_n_llhttp__internal__n_error_105; default: goto s_n_llhttp__internal__n_res_status_code_digit_2; } UNREACHABLE; } - s_n_llhttp__internal__n_error_105: { + s_n_llhttp__internal__n_error_106: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; @@ -9867,7 +9877,7 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - s_n_llhttp__internal__n_error_106: { + s_n_llhttp__internal__n_error_107: { state->error = 0x9; state->reason = "Expected space after version"; state->error_pos = (const char*) p; @@ -9883,7 +9893,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_94: { + s_n_llhttp__internal__n_error_95: { state->error = 0x21; state->reason = "`on_version_complete` callback error"; state->error_pos = (const char*) p; @@ -9917,10 +9927,10 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_93; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_94; return s_error; } - goto s_n_llhttp__internal__n_error_93; + goto s_n_llhttp__internal__n_error_94; UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_http_minor_3: { @@ -9991,10 +10001,10 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_107; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_108; return s_error; } - goto s_n_llhttp__internal__n_error_107; + goto s_n_llhttp__internal__n_error_108; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version_8: { @@ -10007,10 +10017,10 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_108; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_109; return s_error; } - goto s_n_llhttp__internal__n_error_108; + goto s_n_llhttp__internal__n_error_109; UNREACHABLE; } s_n_llhttp__internal__n_invoke_store_http_major_1: { @@ -10030,13 +10040,13 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_109; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_110; return s_error; } - goto s_n_llhttp__internal__n_error_109; + goto s_n_llhttp__internal__n_error_110; UNREACHABLE; } - s_n_llhttp__internal__n_error_114: { + s_n_llhttp__internal__n_error_115: { state->error = 0x8; state->reason = "Expected HTTP/, RTSP/ or ICE/"; state->error_pos = (const char*) p; @@ -10052,7 +10062,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_113: { + s_n_llhttp__internal__n_error_114: { state->error = 0x26; state->reason = "`on_protocol_complete` callback error"; state->error_pos = (const char*) p; @@ -10086,10 +10096,10 @@ static llparse_state_t llhttp__internal__run( if (err != 0) { state->error = err; state->error_pos = (const char*) p; - state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_115; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_116; return s_error; } - goto s_n_llhttp__internal__n_error_115; + goto s_n_llhttp__internal__n_error_116; UNREACHABLE; } s_n_llhttp__internal__n_pause_26: { @@ -10138,7 +10148,7 @@ static llparse_state_t llhttp__internal__run( } UNREACHABLE; } - s_n_llhttp__internal__n_error_110: { + s_n_llhttp__internal__n_error_111: { state->error = 0x8; state->reason = "Invalid word encountered"; state->error_pos = (const char*) p; @@ -10204,7 +10214,7 @@ static llparse_state_t llhttp__internal__run( return s_error; UNREACHABLE; } - s_n_llhttp__internal__n_error_116: { + s_n_llhttp__internal__n_error_117: { state->error = 0x1f; state->reason = "`on_reset` callback error"; state->error_pos = (const char*) p; @@ -10219,7 +10229,7 @@ static llparse_state_t llhttp__internal__run( case 21: goto s_n_llhttp__internal__n_pause_32; default: - goto s_n_llhttp__internal__n_error_116; + goto s_n_llhttp__internal__n_error_117; } UNREACHABLE; } diff --git a/deps/undici/src/lib/cache/memory-cache-store.js b/deps/undici/src/lib/cache/memory-cache-store.js index dba29ae4de9..17a2b84904a 100644 --- a/deps/undici/src/lib/cache/memory-cache-store.js +++ b/deps/undici/src/lib/cache/memory-cache-store.js @@ -218,17 +218,62 @@ class MemoryCacheStore extends EventEmitter { } function findEntry (key, entries, now) { - return entries.find((entry) => ( - entry.deleteAt > now && - entry.method === key.method && - (entry.vary == null || Object.keys(entry.vary).every(headerName => { - if (entry.vary[headerName] === null) { - return key.headers[headerName] === undefined + for (let i = 0; i < entries.length; i++) { + const entry = entries[i] + if ( + entry.deleteAt > now && + entry.method === key.method && + varyMatches(key, entry) + ) { + return entry + } + } +} + +function varyMatches (key, entry) { + if (entry.vary == null) { + return true + } + + for (const headerName in entry.vary) { + if (Object.hasOwn(entry.vary, headerName) && !headerValueEquals(key.headers?.[headerName], entry.vary[headerName])) { + return false + } + } + + return true +} + +/** + * @param {string|string[]|null|undefined} lhs + * @param {string|string[]|null|undefined} rhs + * @returns {boolean} + */ +function headerValueEquals (lhs, rhs) { + if (lhs == null && rhs == null) { + return true + } + + if ((lhs == null && rhs != null) || + (lhs != null && rhs == null)) { + return false + } + + if (Array.isArray(lhs) && Array.isArray(rhs)) { + if (lhs.length !== rhs.length) { + return false + } + + for (let i = 0; i < lhs.length; i++) { + if (lhs[i] !== rhs[i]) { + return false } + } + + return true + } - return entry.vary[headerName] === key.headers[headerName] - })) - )) + return lhs === rhs } module.exports = MemoryCacheStore diff --git a/deps/undici/src/lib/cache/sqlite-cache-store.js b/deps/undici/src/lib/cache/sqlite-cache-store.js index 7a5fc999f47..4da06c2dc27 100644 --- a/deps/undici/src/lib/cache/sqlite-cache-store.js +++ b/deps/undici/src/lib/cache/sqlite-cache-store.js @@ -454,7 +454,13 @@ function headerValueEquals (lhs, rhs) { return false } - return lhs.every((x, i) => x === rhs[i]) + for (let i = 0; i < lhs.length; i++) { + if (lhs[i] !== rhs[i]) { + return false + } + } + + return true } return lhs === rhs diff --git a/deps/undici/src/lib/core/request.js b/deps/undici/src/lib/core/request.js index 326d58fb43f..8c9675d60b3 100644 --- a/deps/undici/src/lib/core/request.js +++ b/deps/undici/src/lib/core/request.js @@ -390,7 +390,13 @@ function processHeader (request, key, val) { } else if (typeof val[i] === 'object') { throw new InvalidArgumentError(`invalid ${key} header`) } else { - arr.push(`${val[i]}`) + // Coerce primitives (and reject unsafe coercions such as functions + // with a crafted toString/Symbol.toPrimitive). + const str = `${val[i]}` + if (!isValidHeaderValue(str)) { + throw new InvalidArgumentError(`invalid ${key} header`) + } + arr.push(str) } } val = arr @@ -401,7 +407,12 @@ function processHeader (request, key, val) { } else if (val === null) { val = '' } else { + // Coerce primitives (and reject unsafe coercions such as functions + // with a crafted toString/Symbol.toPrimitive). val = `${val}` + if (!isValidHeaderValue(val)) { + throw new InvalidArgumentError(`invalid ${key} header`) + } } if (headerName === 'host') { diff --git a/deps/undici/src/lib/dispatcher/client-h1.js b/deps/undici/src/lib/dispatcher/client-h1.js index 7d179967110..5b2cdd8faf5 100644 --- a/deps/undici/src/lib/dispatcher/client-h1.js +++ b/deps/undici/src/lib/dispatcher/client-h1.js @@ -10,6 +10,7 @@ const { RequestContentLengthMismatchError, ResponseContentLengthMismatchError, RequestAbortedError, + InvalidArgumentError, HeadersTimeoutError, HeadersOverflowError, SocketError, @@ -1134,8 +1135,16 @@ function writeH1 (client, request) { } body = bodyStream.stream contentLength = bodyStream.length - } else if (util.isBlobLike(body) && request.contentType == null && body.type) { - headers.push('content-type', body.type) + } else if (util.isBlobLike(body) && request.contentType == null) { + const contentType = body.type + if (contentType) { + const contentTypeValue = `${contentType}` + if (!util.isValidHeaderValue(contentTypeValue)) { + util.errorRequest(client, request, new InvalidArgumentError('invalid content-type header')) + return false + } + headers.push('content-type', contentTypeValue) + } } if (body && typeof body.read === 'function') { diff --git a/deps/undici/src/lib/handler/cache-handler.js b/deps/undici/src/lib/handler/cache-handler.js index 8cfe073503a..d9ea5479c39 100644 --- a/deps/undici/src/lib/handler/cache-handler.js +++ b/deps/undici/src/lib/handler/cache-handler.js @@ -3,7 +3,10 @@ const util = require('../core/util') const { parseCacheControlHeader, + hasInvalidCacheControlDirective, parseVaryHeader, + hasVaryStar, + isInvalidOrWildcardVaryHeader, isEtagUsable } = require('../util/cache') const { parseHttpDate } = require('../util/date.js') @@ -26,6 +29,92 @@ const NOT_UNDERSTOOD_STATUS_CODES = [ const MAX_RESPONSE_AGE = 2147483647000 +function trimOWS (value) { + return value.replace(/^[\t ]+|[\t ]+$/g, '') +} + +function arrayIncludes (array, value) { + for (let i = 0; i < array.length; i++) { + if (array[i] === value) { + return true + } + } + + return false +} + +function appendConnectionHeaderTokens (headersToRemove, connectionHeader) { + const values = Array.isArray(connectionHeader) ? connectionHeader : [connectionHeader] + + for (let i = 0; i < values.length; i++) { + const tokens = values[i].split(',') + for (let j = 0; j < tokens.length; j++) { + headersToRemove.push(trimOWS(tokens[j]).toLowerCase()) + } + } +} + +function getSameOriginPath (cacheKey, location) { + if (typeof location !== 'string') { + return undefined + } + + let originUrl + let requestUrl + let locationUrl + try { + originUrl = new URL(cacheKey.origin) + requestUrl = new URL(cacheKey.path, originUrl) + locationUrl = new URL(location, requestUrl) + } catch { + return undefined + } + + if (locationUrl.origin !== originUrl.origin) { + return undefined + } + + return locationUrl.pathname + locationUrl.search +} + +function deleteCachedUri (store, cacheKey, path) { + deleteCachedValue(store, { + ...cacheKey, + path + }) + + for (let i = 0; i < util.safeHTTPMethods.length; i++) { + const method = util.safeHTTPMethods[i] + if (method !== cacheKey.method) { + deleteCachedValue(store, { + ...cacheKey, + method, + path + }) + } + } +} + +function deleteLocationTargets (store, cacheKey, headerValue) { + if (headerValue === undefined) { + return + } + + const values = Array.isArray(headerValue) ? headerValue : [headerValue] + for (let i = 0; i < values.length; i++) { + const path = getSameOriginPath(cacheKey, values[i]) + if (path !== undefined) { + deleteCachedUri(store, cacheKey, path) + } + } +} + +function invalidateUnsafeRequest (store, cacheKey, resHeaders) { + deleteCachedUri(store, cacheKey, cacheKey.path) + deleteLocationTargets(store, cacheKey, resHeaders.location) + deleteLocationTargets(store, cacheKey, resHeaders['content-location']) +} + /** * @typedef {import('../../types/dispatcher.d.ts').default.DispatchHandler} DispatchHandler * @@ -107,28 +196,28 @@ class CacheHandler { const handler = this if ( - !util.safeHTTPMethods.includes(this.#cacheKey.method) && + !arrayIncludes(util.safeHTTPMethods, this.#cacheKey.method) && statusCode >= 200 && statusCode <= 399 ) { // Successful response to an unsafe method, delete it from cache // https://www.rfc-editor.org/rfc/rfc9111.html#name-invalidating-stored-response - try { - this.#store.delete(this.#cacheKey)?.catch?.(noop) - } catch { - // Fail silently - } + invalidateUnsafeRequest(this.#store, this.#cacheKey, resHeaders) return downstreamOnHeaders() } const cacheControlHeader = resHeaders['cache-control'] - const heuristicallyCacheable = resHeaders['last-modified'] && HEURISTICALLY_CACHEABLE_STATUS_CODES.includes(statusCode) + const heuristicallyCacheable = resHeaders['last-modified'] && arrayIncludes(HEURISTICALLY_CACHEABLE_STATUS_CODES, statusCode) if ( !cacheControlHeader && !resHeaders['expires'] && !heuristicallyCacheable && !this.#cacheByDefault ) { + if (statusCode === 304 && resHeaders.vary && isInvalidOrWildcardVaryHeader(resHeaders.vary)) { + deleteCachedValue(this.#store, this.#cacheKey) + } + // Don't have anything to tell us this response is cachable and we're not // caching by default return downstreamOnHeaders() @@ -136,31 +225,46 @@ class CacheHandler { const cacheControlDirectives = cacheControlHeader ? parseCacheControlHeader(cacheControlHeader) : {} if (!canCacheResponse(this.#cacheType, statusCode, resHeaders, cacheControlDirectives, this.#cacheKey.headers)) { + if (statusCode === 304 && (cacheControlHeader || revalidationResponseDisallowsCachedReuse(this.#cacheType, resHeaders, cacheControlDirectives))) { + deleteCachedValue(this.#store, this.#cacheKey) + } + return downstreamOnHeaders() } const now = Date.now() - const resAge = resHeaders.age ? getAge(resHeaders.age) : undefined - if (resAge && resAge >= MAX_RESPONSE_AGE) { + const resAge = Object.hasOwn(resHeaders, 'age') ? getAge(resHeaders.age) : undefined + if (resAge !== undefined && resAge >= MAX_RESPONSE_AGE) { // Response considered stale + deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey) return downstreamOnHeaders() } - const resDate = typeof resHeaders.date === 'string' - ? parseHttpDate(resHeaders.date) - : undefined + const resDate = Object.hasOwn(resHeaders, 'date') ? getDate(resHeaders.date) : undefined + if (resDate === null) { + deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey) + return downstreamOnHeaders() + } + + const apparentAge = resDate ? Math.max(0, now - resDate.getTime()) : 0 + const currentAge = Math.max(apparentAge, resAge ?? 0) const staleAt = determineStaleAt(this.#cacheType, now, resAge, resHeaders, resDate, cacheControlDirectives) ?? this.#cacheByDefault - if (staleAt === undefined || (resAge && resAge > staleAt)) { + if (staleAt === undefined || currentAge >= staleAt) { + if (cacheControlHeader || staleAt !== undefined) { + deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey) + } + return downstreamOnHeaders() } - const baseTime = resDate ? resDate.getTime() : now + const baseTime = now - currentAge const absoluteStaleAt = staleAt + baseTime if (now >= absoluteStaleAt) { // Response is already stale + deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey) return downstreamOnHeaders() } @@ -173,7 +277,8 @@ class CacheHandler { } } - const deleteAt = determineDeleteAt(baseTime, cacheControlDirectives, absoluteStaleAt) + const cachedAt = baseTime + const deleteAt = determineDeleteAt(baseTime, now, cacheControlDirectives, absoluteStaleAt) const strippedHeaders = stripNecessaryHeaders(resHeaders, cacheControlDirectives) /** @@ -185,7 +290,7 @@ class CacheHandler { headers: strippedHeaders, vary: varyDirectives, cacheControlDirectives, - cachedAt: resAge ? now - resAge : now, + cachedAt, staleAt: absoluteStaleAt, deleteAt } @@ -203,6 +308,7 @@ class CacheHandler { value.statusCode = cachedValue.statusCode value.statusMessage = cachedValue.statusMessage value.etag = cachedValue.etag + value.vary = varyDirectives ?? cachedValue.vary value.headers = { ...cachedValue.headers, ...strippedHeaders } downstreamOnHeaders() @@ -333,6 +439,36 @@ class CacheHandler { } } +/** + * @param {import('../../types/cache-interceptor.d.ts').default.CacheStore} store + * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey + */ +function deleteCachedValue (store, cacheKey) { + try { + store.delete(cacheKey)?.catch?.(noop) + } catch { + // Fail silently + } +} + +function deleteCachedValueIfNotModified (statusCode, store, cacheKey) { + if (statusCode === 304) { + deleteCachedValue(store, cacheKey) + } +} + +/** + * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType + * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders + * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives + * @returns {boolean} + */ +function revalidationResponseDisallowsCachedReuse (cacheType, resHeaders, cacheControlDirectives) { + return cacheControlDirectives['no-store'] === true || + (cacheType === 'shared' && cacheControlDirectives.private === true) || + (resHeaders.vary ? isInvalidOrWildcardVaryHeader(resHeaders.vary) : false) +} + /** * @see https://www.rfc-editor.org/rfc/rfc9111.html#name-storing-responses-to-authen * @@ -344,12 +480,12 @@ class CacheHandler { */ function canCacheResponse (cacheType, statusCode, resHeaders, cacheControlDirectives, reqHeaders) { // Status code must be final and understood. - if (statusCode < 200 || NOT_UNDERSTOOD_STATUS_CODES.includes(statusCode)) { + if (statusCode < 200 || arrayIncludes(NOT_UNDERSTOOD_STATUS_CODES, statusCode)) { return false } // Responses with neither status codes that are heuristically cacheable, nor "explicit enough" caching // directives, are not cacheable. "Explicit enough": see https://www.rfc-editor.org/rfc/rfc9111.html#section-3 - if (!HEURISTICALLY_CACHEABLE_STATUS_CODES.includes(statusCode) && !resHeaders['expires'] && + if (!arrayIncludes(HEURISTICALLY_CACHEABLE_STATUS_CODES, statusCode) && !resHeaders['expires'] && !cacheControlDirectives.public && cacheControlDirectives['max-age'] === undefined && // RFC 9111: a private response directive, if the cache is not shared @@ -368,12 +504,12 @@ function canCacheResponse (cacheType, statusCode, resHeaders, cacheControlDirect } // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.1-5 - if (resHeaders.vary?.includes('*')) { + if (resHeaders.vary && hasVaryStar(resHeaders.vary)) { return false } // https://www.rfc-editor.org/rfc/rfc9111.html#name-storing-responses-to-authen - if (reqHeaders?.authorization) { + if (reqHeaders != null && Object.hasOwn(reqHeaders, 'authorization')) { if ( !cacheControlDirectives.public && !cacheControlDirectives['s-maxage'] && @@ -388,14 +524,14 @@ function canCacheResponse (cacheType, statusCode, resHeaders, cacheControlDirect if ( Array.isArray(cacheControlDirectives['no-cache']) && - cacheControlDirectives['no-cache'].includes('authorization') + arrayIncludes(cacheControlDirectives['no-cache'], 'authorization') ) { return false } if ( Array.isArray(cacheControlDirectives['private']) && - cacheControlDirectives['private'].includes('authorization') + arrayIncludes(cacheControlDirectives['private'], 'authorization') ) { return false } @@ -404,14 +540,51 @@ function canCacheResponse (cacheType, statusCode, resHeaders, cacheControlDirect return true } +/** + * @param {string | string[]} dateHeader + * @returns {Date | null | undefined} + */ +function getDate (dateHeader) { + let dateValue = dateHeader + if (Array.isArray(dateValue)) { + if (dateValue.length !== 1) { + return null + } + + dateValue = dateValue[0] + } + + if (typeof dateValue !== 'string') { + return null + } + + return parseHttpDate(dateValue) +} + /** * @param {string | string[]} ageHeader * @returns {number | undefined} */ function getAge (ageHeader) { - const age = parseInt(Array.isArray(ageHeader) ? ageHeader[0] : ageHeader) + let ageValue = ageHeader + if (Array.isArray(ageValue)) { + if (ageValue.length !== 1) { + return MAX_RESPONSE_AGE + } + + ageValue = ageValue[0] + } - return isNaN(age) ? undefined : age * 1000 + if (typeof ageValue !== 'string' || !/^[\t ]*[0-9]+[\t ]*$/.test(ageValue)) { + return MAX_RESPONSE_AGE + } + + const age = BigInt(ageValue.replace(/^[\t ]+|[\t ]+$/g, '')) + if (age >= BigInt(MAX_RESPONSE_AGE / 1000)) { + return MAX_RESPONSE_AGE + } + + return Number(age) * 1000 } /** @@ -429,43 +602,60 @@ function determineStaleAt (cacheType, now, age, resHeaders, responseDate, cacheC // Prioritize s-maxage since we're a shared cache // s-maxage > max-age > Expire // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.10-3 + if (hasInvalidCacheControlDirective(cacheControlDirectives, 's-maxage')) { + return 0 + } + const sMaxAge = cacheControlDirectives['s-maxage'] if (sMaxAge !== undefined) { - return sMaxAge > 0 ? sMaxAge * 1000 : undefined + return sMaxAge * 1000 } } + if (hasInvalidCacheControlDirective(cacheControlDirectives, 'max-age')) { + return 0 + } + const maxAge = cacheControlDirectives['max-age'] if (maxAge !== undefined) { - return maxAge > 0 ? maxAge * 1000 : undefined + return maxAge * 1000 } - if (typeof resHeaders.expires === 'string') { + if (Object.hasOwn(resHeaders, 'expires')) { // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3 + if (typeof resHeaders.expires !== 'string') { + return 0 + } + const expiresDate = parseHttpDate(resHeaders.expires) - if (expiresDate) { - if (now >= expiresDate.getTime()) { - return undefined - } + if (!expiresDate) { + return 0 + } - if (responseDate) { - if (responseDate >= expiresDate) { - return undefined - } + if (now >= expiresDate.getTime()) { + return 0 + } - if (age !== undefined && age > (expiresDate - responseDate)) { - return undefined - } + if (responseDate) { + if (responseDate >= expiresDate) { + return 0 + } + + const freshnessLifetime = expiresDate.getTime() - responseDate.getTime() + if (age !== undefined && age >= freshnessLifetime) { + return 0 } - return expiresDate.getTime() - now + return freshnessLifetime } + + return expiresDate.getTime() - now } if (typeof resHeaders['last-modified'] === 'string') { // https://www.rfc-editor.org/rfc/rfc9111.html#name-calculating-heuristic-fresh - const lastModified = new Date(resHeaders['last-modified']) - if (isValidDate(lastModified)) { + const lastModified = parseHttpDate(resHeaders['last-modified']) + if (lastModified) { if (lastModified.getTime() >= now) { return undefined } @@ -478,18 +668,19 @@ function determineStaleAt (cacheType, now, age, resHeaders, responseDate, cacheC if (cacheControlDirectives.immutable) { // https://www.rfc-editor.org/rfc/rfc8246.html#section-2.2 - return 31536000 + return 31536000000 } return undefined } /** - * @param {number} now + * @param {number} baseTime + * @param {number} cachedAt * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives * @param {number} staleAt */ -function determineDeleteAt (now, cacheControlDirectives, staleAt) { +function determineDeleteAt (baseTime, cachedAt, cacheControlDirectives, staleAt) { let staleWhileRevalidate = -Infinity let staleIfError = -Infinity let immutable = -Infinity @@ -503,15 +694,21 @@ function determineDeleteAt (now, cacheControlDirectives, staleAt) { } if (cacheControlDirectives.immutable && staleWhileRevalidate === -Infinity && staleIfError === -Infinity) { - immutable = now + 31536000000 + immutable = cachedAt + 31536000000 } // When no stale directives or immutable flag, add a revalidation buffer // equal to the freshness lifetime so the entry survives past staleAt long // enough to be revalidated instead of silently disappearing. + // + // Response Date headers only have second precision, so baseTime can trail the + // actual cache insertion time by up to ~1s. Pad the buffer by that bounded + // skew so short-lived entries do not disappear exactly when they should be + // revalidated. if (staleWhileRevalidate === -Infinity && staleIfError === -Infinity && immutable === -Infinity) { - const freshnessLifetime = staleAt - now - return staleAt + freshnessLifetime + const freshnessLifetime = staleAt - baseTime + const datePrecisionPadding = Math.min(Math.max(cachedAt - baseTime, 0), 1000) + return staleAt + freshnessLifetime + datePrecisionPadding } return Math.max(staleAt, staleWhileRevalidate, staleIfError, immutable) @@ -538,14 +735,7 @@ function stripNecessaryHeaders (resHeaders, cacheControlDirectives) { ] if (resHeaders['connection']) { - if (Array.isArray(resHeaders['connection'])) { - // connection: a - // connection: b - headersToRemove.push(...resHeaders['connection'].map(header => header.trim())) - } else { - // connection: a, b - headersToRemove.push(...resHeaders['connection'].split(',').map(header => header.trim())) - } + appendConnectionHeaderTokens(headersToRemove, resHeaders['connection']) } if (Array.isArray(cacheControlDirectives['no-cache'])) { @@ -558,7 +748,7 @@ function stripNecessaryHeaders (resHeaders, cacheControlDirectives) { let strippedHeaders for (const headerName of headersToRemove) { - if (resHeaders[headerName]) { + if (Object.hasOwn(resHeaders, headerName)) { strippedHeaders ??= { ...resHeaders } delete strippedHeaders[headerName] } @@ -567,12 +757,4 @@ function stripNecessaryHeaders (resHeaders, cacheControlDirectives) { return strippedHeaders ?? resHeaders } -/** - * @param {Date} date - * @returns {boolean} - */ -function isValidDate (date) { - return date instanceof Date && Number.isFinite(date.valueOf()) -} - module.exports = CacheHandler diff --git a/deps/undici/src/lib/handler/cache-revalidation-handler.js b/deps/undici/src/lib/handler/cache-revalidation-handler.js index 393d16d52c6..8858d8e2f8e 100644 --- a/deps/undici/src/lib/handler/cache-revalidation-handler.js +++ b/deps/undici/src/lib/handler/cache-revalidation-handler.js @@ -19,7 +19,7 @@ class CacheRevalidationHandler { #successful = false /** - * @type {((boolean, any) => void) | null} + * @type {((success: boolean, context?: any, statusCode?: number, headers?: import('../../types/header.d.ts').IncomingHttpHeaders) => void) | null} */ #callback @@ -36,7 +36,7 @@ class CacheRevalidationHandler { #allowErrorStatusCodes /** - * @param {(boolean) => void} callback Function to call if the cached value is valid + * @param {(success: boolean, context?: any, statusCode?: number, headers?: import('../../types/header.d.ts').IncomingHttpHeaders) => void} callback Function to call if the cached value is valid * @param {import('../../types/dispatcher.d.ts').default.DispatchHandlers} handler * @param {boolean} allowErrorStatusCodes */ @@ -71,7 +71,7 @@ class CacheRevalidationHandler { // https://datatracker.ietf.org/doc/html/rfc5861#section-4 this.#successful = statusCode === 304 || (this.#allowErrorStatusCodes && statusCode >= 500 && statusCode <= 504) - this.#callback(this.#successful, this.#context) + this.#callback(this.#successful, this.#context, statusCode, headers) this.#callback = null if (this.#successful) { diff --git a/deps/undici/src/lib/handler/retry-handler.js b/deps/undici/src/lib/handler/retry-handler.js index 1cbc78981b1..8908ce5b4cc 100644 --- a/deps/undici/src/lib/handler/retry-handler.js +++ b/deps/undici/src/lib/handler/retry-handler.js @@ -15,6 +15,26 @@ function calculateRetryAfterHeader (retryAfter) { return isNaN(retryTime) ? 0 : retryTime - Date.now() } +function validatePartialResponseContentLength (headers, range, statusCode, retryCount) { + const contentLength = headers['content-length'] + if (contentLength == null) { + return + } + + if (!Number.isFinite(range.start) || !Number.isFinite(range.end)) { + return + } + + const length = Number(contentLength) + const expectedLength = range.end - range.start + 1 + if (!Number.isFinite(length) || length !== expectedLength) { + throw new RequestRetryError('Content-Length mismatch', statusCode, { + headers, + data: { count: retryCount } + }) + } +} + class RetryHandler { constructor (opts, { dispatch, handler }) { const { retryOptions, ...dispatchOpts } = opts @@ -229,6 +249,8 @@ class RetryHandler { }) } + validatePartialResponseContentLength(headers, contentRange, statusCode, this.retryCount) + const { start, size, end = size ? size - 1 : null } = contentRange assert(this.start === start, 'content-range mismatch') @@ -253,6 +275,8 @@ class RetryHandler { return } + validatePartialResponseContentLength(headers, range, statusCode, this.retryCount) + const { start, size, end = size ? size - 1 : null } = range assert( start != null && Number.isFinite(start), diff --git a/deps/undici/src/lib/interceptor/cache.js b/deps/undici/src/lib/interceptor/cache.js index 81d7cb12cbb..a686cbf4102 100644 --- a/deps/undici/src/lib/interceptor/cache.js +++ b/deps/undici/src/lib/interceptor/cache.js @@ -6,8 +6,9 @@ const util = require('../core/util') const CacheHandler = require('../handler/cache-handler') const MemoryCacheStore = require('../cache/memory-cache-store') const CacheRevalidationHandler = require('../handler/cache-revalidation-handler') -const { assertCacheStore, assertCacheMethods, makeCacheKey, normalizeHeaders, parseCacheControlHeader } = require('../util/cache.js') +const { assertCacheStore, assertCacheMethods, makeCacheKey, normalizeHeaders, parseCacheControlHeader, isInvalidOrWildcardVaryHeader } = require('../util/cache.js') const { AbortError } = require('../core/errors.js') +const { parseHttpDate } = require('../util/date.js') /** * @param {(string | RegExp)[] | undefined} origins @@ -28,6 +29,44 @@ function assertCacheOrigins (origins, name) { const nop = () => {} +function trimOWS (value) { + return value.replace(/^[\t ]+|[\t ]+$/g, '') +} + +function arrayIncludes (array, value) { + for (let i = 0; i < array.length; i++) { + if (array[i] === value) { + return true + } + } + + return false +} + +function hasPragmaNoCache (headers) { + const pragma = headers?.pragma + if (!pragma) { + return false + } + + const values = Array.isArray(pragma) ? pragma : [pragma] + for (let i = 0; i < values.length; i++) { + const value = values[i] + if (typeof value !== 'string') { + continue + } + + const directives = value.split(',') + for (let j = 0; j < directives.length; j++) { + if (trimOWS(directives[j]).toLowerCase() === 'no-cache') { + return true + } + } + } + + return false +} + /** * @typedef {(options: import('../../types/dispatcher.d.ts').default.DispatchOptions, handler: import('../../types/dispatcher.d.ts').default.DispatchHandler) => void} DispatchFn */ @@ -57,16 +96,92 @@ function needsRevalidation (result, cacheControlDirectives, { headers = {} }) { return false } +/** + * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result + * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType + * @returns {boolean} + */ +function staleResponseRequiresRevalidation (result, cacheType) { + return result.cacheControlDirectives?.['must-revalidate'] === true || + (cacheType === 'shared' && ( + result.cacheControlDirectives?.['proxy-revalidate'] === true || + // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.10 + // s-maxage implies proxy-revalidate for shared caches. + result.cacheControlDirectives?.['s-maxage'] !== undefined + )) +} + +/** + * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType + * @param {import('../../types/header.d.ts').IncomingHttpHeaders} headers + * @returns {boolean} + */ +function revalidationResponseDisallowsCachedReuse (cacheType, headers) { + if (headers.vary && isInvalidOrWildcardVaryHeader(headers.vary)) { + return true + } + + const cacheControl = headers['cache-control'] + if (!cacheControl) { + return false + } + + const cacheControlDirectives = parseCacheControlHeader(cacheControl) + return cacheControlDirectives['no-store'] === true || + (cacheType === 'shared' && cacheControlDirectives.private === true) +} + +function revalidationResponseUpdatesCacheControl (headers) { + return headers['cache-control'] !== undefined +} + +function deleteCachedValue (store, cacheKey) { + try { + store.delete(cacheKey)?.catch?.(nop) + } catch { + // Fail silently + } +} + +function getUsableLastModified (headers) { + const lastModified = headers?.['last-modified'] + if (typeof lastModified === 'string' && parseHttpDate(lastModified)) { + return lastModified + } +} + +function makeRevalidationHeaders (opts, result) { + const headers = { + ...opts.headers, + 'if-modified-since': getUsableLastModified(result.headers) ?? new Date(result.cachedAt).toUTCString() + } + + if (result.etag) { + headers['if-none-match'] = result.etag + } + + if (result.vary) { + for (const key in result.vary) { + if (result.vary[key] != null) { + headers[key] = result.vary[key] + } + } + } + + return headers +} + /** * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives | undefined} cacheControlDirectives + * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType * @returns {boolean} */ -function isStale (result, cacheControlDirectives) { +function isStale (result, cacheControlDirectives, cacheType) { const now = Date.now() if (now > result.staleAt) { // Response is stale - if (cacheControlDirectives?.['max-stale']) { + if (!staleResponseRequiresRevalidation(result, cacheType) && cacheControlDirectives?.['max-stale']) { // There's a threshold where we can serve stale responses, let's see if // we're in it // https://www.rfc-editor.org/rfc/rfc9111.html#name-max-stale @@ -93,11 +208,12 @@ function isStale (result, cacheControlDirectives) { /** * Check if we're within the stale-while-revalidate window for a stale response * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result + * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType * @returns {boolean} */ -function withinStaleWhileRevalidateWindow (result) { +function withinStaleWhileRevalidateWindow (result, cacheType) { const staleWhileRevalidate = result.cacheControlDirectives?.['stale-while-revalidate'] - if (!staleWhileRevalidate) { + if (!staleWhileRevalidate || staleResponseRequiresRevalidation(result, cacheType)) { return false } @@ -267,14 +383,10 @@ function handleResult ( } const age = Math.round((now - result.cachedAt) / 1000) - if (reqCacheControl?.['max-age'] && age >= reqCacheControl['max-age']) { - // Response is considered expired for this specific request - // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.1 - return dispatch(opts, handler) - } + const requestMaxAgeExpired = reqCacheControl?.['max-age'] !== undefined && age >= reqCacheControl['max-age'] - const stale = isStale(result, reqCacheControl) - const revalidate = needsRevalidation(result, reqCacheControl, opts) + const stale = requestMaxAgeExpired || isStale(result, reqCacheControl, globalOpts.type) + const revalidate = requestMaxAgeExpired || needsRevalidation(result, reqCacheControl, opts) // Check if the response is stale if (stale || revalidate) { @@ -286,28 +398,13 @@ function handleResult ( // RFC 5861: If we're within stale-while-revalidate window, serve stale immediately // and revalidate in background, unless immediate revalidation is necessary - if (!revalidate && withinStaleWhileRevalidateWindow(result)) { + if (!revalidate && withinStaleWhileRevalidateWindow(result, globalOpts.type)) { // Serve stale response immediately sendCachedValue(handler, opts, result, age, null, true) // Start background revalidation (fire-and-forget) queueMicrotask(() => { - const headers = { - ...opts.headers, - 'if-modified-since': new Date(result.cachedAt).toUTCString() - } - - if (result.etag) { - headers['if-none-match'] = result.etag - } - - if (result.vary) { - for (const key in result.vary) { - if (result.vary[key] != null) { - headers[key] = result.vary[key] - } - } - } + const headers = makeRevalidationHeaders(opts, result) // Background revalidation - update cache if we get new data dispatch( @@ -331,28 +428,15 @@ function handleResult ( } let withinStaleIfErrorThreshold = false - const staleIfErrorExpiry = result.cacheControlDirectives['stale-if-error'] ?? reqCacheControl?.['stale-if-error'] - if (staleIfErrorExpiry) { - withinStaleIfErrorThreshold = now < (result.staleAt + (staleIfErrorExpiry * 1000)) - } - - const headers = { - ...opts.headers, - 'if-modified-since': new Date(result.cachedAt).toUTCString() - } - - if (result.etag) { - headers['if-none-match'] = result.etag - } - - if (result.vary) { - for (const key in result.vary) { - if (result.vary[key] != null) { - headers[key] = result.vary[key] - } + if (!staleResponseRequiresRevalidation(result, globalOpts.type)) { + const staleIfErrorExpiry = result.cacheControlDirectives['stale-if-error'] ?? reqCacheControl?.['stale-if-error'] + if (staleIfErrorExpiry) { + withinStaleIfErrorThreshold = now < (result.staleAt + (staleIfErrorExpiry * 1000)) } } + const headers = makeRevalidationHeaders(opts, result) + // We need to revalidate the response return dispatch( { @@ -360,8 +444,23 @@ function handleResult ( headers }, new CacheRevalidationHandler( - (success, context) => { + (success, context, statusCode, headers) => { if (success) { + if (statusCode === 304) { + if (revalidationResponseDisallowsCachedReuse(globalOpts.type, headers)) { + if (util.isStream(result.body)) { + result.body.on('error', nop).destroy() + } + + deleteCachedValue(globalOpts.store, cacheKey) + return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler)) + } + + if (revalidationResponseUpdatesCacheControl(headers)) { + deleteCachedValue(globalOpts.store, cacheKey) + } + } + // TODO: successful revalidation should be considered fresh (not give stale warning). sendCachedValue(handler, opts, result, age, context, stale) } else if (util.isStream(result.body)) { @@ -418,11 +517,17 @@ module.exports = (opts = {}) => { type } - const safeMethodsToNotCache = util.safeHTTPMethods.filter(method => methods.includes(method) === false) + const safeMethodsToNotCache = [] + for (let i = 0; i < util.safeHTTPMethods.length; i++) { + const method = util.safeHTTPMethods[i] + if (!arrayIncludes(methods, method)) { + safeMethodsToNotCache.push(method) + } + } return dispatch => { return (opts, handler) => { - if (!opts.origin || safeMethodsToNotCache.includes(opts.method)) { + if (!opts.origin || arrayIncludes(safeMethodsToNotCache, opts.method)) { // Not a method we want to cache or we don't have the origin, skip return dispatch(opts, handler) } @@ -457,7 +562,9 @@ module.exports = (opts = {}) => { const reqCacheControl = opts.headers?.['cache-control'] ? parseCacheControlHeader(opts.headers['cache-control']) - : undefined + : hasPragmaNoCache(opts.headers) + ? { 'no-cache': true } + : undefined if (reqCacheControl?.['no-store']) { return dispatch(opts, handler) diff --git a/deps/undici/src/lib/llhttp/wasm_build_env.txt b/deps/undici/src/lib/llhttp/wasm_build_env.txt index 80834a2d861..0b2f32dd902 100644 --- a/deps/undici/src/lib/llhttp/wasm_build_env.txt +++ b/deps/undici/src/lib/llhttp/wasm_build_env.txt @@ -1,5 +1,5 @@ -> undici@7.28.0 build:wasm +> undici@7.29.0 build:wasm > node build/wasm.js --docker > docker run --rm --platform=linux/x86_64 --user 1001:1001 --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/lib/llhttp,target=/home/node/build/lib/llhttp --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/build,target=/home/node/build/build --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/deps,target=/home/node/build/deps -t ghcr.io/nodejs/wasm-builder@sha256:975f391d907e42a75b8c72eb77c782181e941608687d4d8694c3e9df415a0970 node build/wasm.js diff --git a/deps/undici/src/lib/util/cache.js b/deps/undici/src/lib/util/cache.js index a8f112168e6..da8ae122873 100644 --- a/deps/undici/src/lib/util/cache.js +++ b/deps/undici/src/lib/util/cache.js @@ -3,11 +3,147 @@ const { safeHTTPMethods, pathHasQueryOrFragment, - hasSafeIterator + hasSafeIterator, + isValidHTTPToken } = require('../core/util') const { serializePathWithQuery } = require('../core/util') +const MAX_DELTA_SECONDS = 2147483647 +const RESTRICTIVE_DIRECTIVE_NAMES = ['no-store', 'private', 'no-cache'] +const kInvalidCacheControlDirectives = Symbol('invalid cache-control directives') + +function trimOWS (value) { + return value.replace(/^[\t ]+|[\t ]+$/g, '') +} + +function arrayIncludes (array, value) { + for (let i = 0; i < array.length; i++) { + if (array[i] === value) { + return true + } + } + + return false +} + +function trimOWSStart (value) { + return value.replace(/^[\t ]+/, '') +} + +function trimOWSEnd (value) { + return value.replace(/[\t ]+$/, '') +} + +function findUnescapedQuote (value, start) { + let escaped = false + for (let i = start; i < value.length; i++) { + if (escaped) { + escaped = false + } else if (value[i] === '\\') { + escaped = true + } else if (value[i] === '"') { + return i + } + } + + return -1 +} + +function splitCacheControlHeaderValue (value) { + const directives = [] + let start = 0 + let quoteStart = -1 + let inQuote = false + let escaped = false + + for (let i = 0; i < value.length; i++) { + if (inQuote) { + if (escaped) { + escaped = false + } else if (value[i] === '\\') { + escaped = true + } else if (value[i] === '"') { + inQuote = false + quoteStart = -1 + } + } else if (value[i] === '"') { + inQuote = true + quoteStart = i + } else if (value[i] === ',') { + directives.push({ value: value.substring(start, i), fromMalformedQuote: false }) + start = i + 1 + } + } + + if (!inQuote) { + directives.push({ value: value.substring(start), fromMalformedQuote: false }) + return directives + } + + const tail = value.substring(start) + const quoteOffset = quoteStart - start + let tailStart = 0 + for (let i = 0; i < tail.length; i++) { + if (tail[i] === ',') { + directives.push({ + value: tail.substring(tailStart, i), + fromMalformedQuote: tailStart > quoteOffset + }) + tailStart = i + 1 + } + } + + directives.push({ + value: tail.substring(tailStart), + fromMalformedQuote: tailStart > quoteOffset + }) + return directives +} + +function markInvalidCacheControlDirective (directives, key) { + let invalidDirectives = directives[kInvalidCacheControlDirectives] + + if (invalidDirectives === undefined) { + invalidDirectives = new Set() + Object.defineProperty(directives, kInvalidCacheControlDirectives, { + value: invalidDirectives + }) + } + + invalidDirectives.add(key) +} + +function hasInvalidCacheControlDirective (directives, key) { + return directives[kInvalidCacheControlDirectives]?.has(key) === true +} + +function getMalformedRestrictiveDirectiveName (key) { + for (const directiveName of RESTRICTIVE_DIRECTIVE_NAMES) { + if ( + key.startsWith(directiveName) && + key.length > directiveName.length && + !isValidHTTPToken(key[directiveName.length]) + ) { + return directiveName + } + } + + let tokenOnlyKey = '' + let hasInvalidTokenChar = false + for (let i = 0; i < key.length; i++) { + if (isValidHTTPToken(key[i])) { + tokenOnlyKey += key[i] + } else { + hasInvalidTokenChar = true + } + } + + if (hasInvalidTokenChar && arrayIncludes(RESTRICTIVE_DIRECTIVE_NAMES, tokenOnlyKey)) { + return tokenOnlyKey + } +} + /** * @param {import('../../types/dispatcher.d.ts').default.DispatchOptions} opts */ @@ -30,6 +166,20 @@ function makeCacheKey (opts) { } } +function appendHeader (headers, key, val) { + const headerName = key.toLowerCase() + const current = headers[headerName] + const values = Array.isArray(val) ? val : [val] + + if (current === undefined) { + headers[headerName] = Array.isArray(val) ? val.slice() : val + } else if (Array.isArray(current)) { + current.push(...values) + } else { + headers[headerName] = [current, ...values] + } +} + /** * @param {Record} * @returns {Record} @@ -50,11 +200,11 @@ function normalizeHeaders (opts) { if (typeof key !== 'string' || typeof val !== 'string') { throw new Error('opts.headers is not a valid header map') } - headers[key.toLowerCase()] = val + appendHeader(headers, key, val) } } else { for (const key of Object.keys(opts.headers)) { - headers[key.toLowerCase()] = opts.headers[key] + appendHeader(headers, key, opts.headers[key]) } } } else { @@ -126,29 +276,37 @@ function parseCacheControlHeader (header) { * @type {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} */ const output = {} + const invalidNumericDirectives = new Set() + const invalidNoArgumentDirectives = new Set() - let directives - if (Array.isArray(header)) { - directives = [] - - for (const directive of header) { - directives.push(...directive.split(',')) - } - } else { - directives = header.split(',') - } + const directives = splitCacheControlHeaderValue(Array.isArray(header) ? header.join(',') : header) for (let i = 0; i < directives.length; i++) { - const directive = directives[i].toLowerCase() + const directiveRecord = directives[i] + const directive = directiveRecord.value.toLowerCase() + const fromMalformedQuote = directiveRecord.fromMalformedQuote const keyValueDelimiter = directive.indexOf('=') let key let value + let keyHasTrailingWhitespace = false + let valueHasLeadingWhitespace = false if (keyValueDelimiter !== -1) { - key = directive.substring(0, keyValueDelimiter).trimStart() - value = directive.substring(keyValueDelimiter + 1) + const rawKey = directive.substring(0, keyValueDelimiter) + const rawValue = directive.substring(keyValueDelimiter + 1) + + keyHasTrailingWhitespace = trimOWSEnd(rawKey) !== rawKey + valueHasLeadingWhitespace = trimOWSStart(rawValue) !== rawValue + key = trimOWS(rawKey) + value = trimOWSStart(rawValue) } else { - key = directive.trim() + key = trimOWS(directive) + } + + const malformedRestrictiveDirectiveName = getMalformedRestrictiveDirectiveName(key) + if (malformedRestrictiveDirectiveName !== undefined) { + output[malformedRestrictiveDirectiveName] = true + continue } switch (key) { @@ -158,7 +316,14 @@ function parseCacheControlHeader (header) { case 's-maxage': case 'stale-while-revalidate': case 'stale-if-error': { - if (value === undefined || value[0] === ' ') { + if (fromMalformedQuote || invalidNumericDirectives.has(key)) { + continue + } + + if (value === undefined || keyHasTrailingWhitespace || valueHasLeadingWhitespace) { + delete output[key] + invalidNumericDirectives.add(key) + markInvalidCacheControlDirective(output, key) continue } @@ -170,22 +335,37 @@ function parseCacheControlHeader (header) { value = value.substring(1, value.length - 1) } - const parsedValue = parseInt(value, 10) - // eslint-disable-next-line no-self-compare - if (parsedValue !== parsedValue) { + if (!/^[0-9]+$/.test(value)) { + delete output[key] + invalidNumericDirectives.add(key) + markInvalidCacheControlDirective(output, key) continue } - if (key === 'max-age' && key in output && output[key] >= parsedValue) { - continue - } + const parsedValue = Math.min(parseInt(value, 10), MAX_DELTA_SECONDS) - output[key] = parsedValue + if (key === 'min-fresh') { + if (!(key in output) || output[key] < parsedValue) { + output[key] = parsedValue + } + } else if (!(key in output) || output[key] > parsedValue) { + output[key] = parsedValue + } break } case 'private': case 'no-cache': { + if (fromMalformedQuote) { + output[key] = true + break + } + + if (value !== undefined && value.length === 0) { + output[key] = true + break + } + if (value) { // The private and no-cache directives can be unqualified (aka just // `private` or `no-cache`) or qualified (w/ a value). When they're @@ -193,45 +373,64 @@ function parseCacheControlHeader (header) { // `no-cache="header1"`, or `no-cache="header1, header2"` // If we're given multiple headers, the comma messes us up since // we split the full header by commas. So, let's loop through the - // remaining parts in front of us until we find one that ends in a - // quote. We can then just splice all of the parts in between the - // starting quote and the ending quote out of the directives array - // and continue parsing like normal. + // remaining parts in front of us until we find one that contains a + // closing quote. We can then skip the consumed quoted-list fragments and + // continue parsing like normal. // https://www.rfc-editor.org/rfc/rfc9111.html#name-no-cache-2 if (value[0] === '"') { // Something like `no-cache="some-header"` OR `no-cache="some-header, another-header"`. + value = trimOWSEnd(value) - // Add the first header on and cut off the leading quote - const headers = [value.substring(1)] + let fieldList = '' + let lastQuotedPart = i + let foundEndingQuote = false + const closingQuote = findUnescapedQuote(value, 1) - let foundEndingQuote = value[value.length - 1] === '"' - if (!foundEndingQuote) { + if (closingQuote !== -1) { + fieldList = value.substring(1, closingQuote) + foundEndingQuote = true + } else { // Something like `no-cache="some-header, another-header"` // This can still be something invalid, e.g. `no-cache="some-header, ...` + const fieldListParts = [value.substring(1)] + for (let j = i + 1; j < directives.length; j++) { - const nextPart = directives[j] - const nextPartLength = nextPart.length + const nextPart = trimOWS(directives[j].value) + const closingQuote = findUnescapedQuote(nextPart, 0) - headers.push(nextPart.trim()) + lastQuotedPart = j - if (nextPartLength !== 0 && nextPart[nextPartLength - 1] === '"') { + if (closingQuote !== -1) { + fieldListParts.push(nextPart.substring(0, closingQuote)) foundEndingQuote = true break } + + fieldListParts.push(nextPart) } + + fieldList = fieldListParts.join(',') } - if (foundEndingQuote) { - let lastHeader = headers[headers.length - 1] - if (lastHeader[lastHeader.length - 1] === '"') { - lastHeader = lastHeader.substring(0, lastHeader.length - 1) - headers[headers.length - 1] = lastHeader - } + if (!foundEndingQuote) { + output[key] = true + break + } - for (let j = 0; j < headers.length; j++) { - headers[j] = headers[j].trim() + i = lastQuotedPart + + const headers = fieldList.split(',') + let validFieldNames = true + for (let j = 0; j < headers.length; j++) { + headers[j] = trimOWS(headers[j]) + if (!isValidHTTPToken(headers[j])) { + validFieldNames = false } + } + if (!validFieldNames) { + output[key] = true + } else if (output[key] !== true) { if (key in output) { output[key] = output[key].concat(headers) } else { @@ -239,13 +438,17 @@ function parseCacheControlHeader (header) { } } } else { - // Something like `no-cache="some-header"` - const fieldName = value.trim() + // Something like `no-cache=some-header` + const fieldName = trimOWS(value) - if (key in output) { - output[key] = output[key].concat(fieldName) - } else { - output[key] = [fieldName] + if (!isValidHTTPToken(fieldName)) { + output[key] = true + } else if (output[key] !== true) { + if (key in output) { + output[key] = output[key].concat(fieldName) + } else { + output[key] = [fieldName] + } } } @@ -254,19 +457,27 @@ function parseCacheControlHeader (header) { } // eslint-disable-next-line no-fallthrough case 'public': - case 'no-store': case 'must-revalidate': case 'proxy-revalidate': case 'immutable': case 'no-transform': case 'must-understand': case 'only-if-cached': - if (value) { + if (fromMalformedQuote || invalidNoArgumentDirectives.has(key)) { + continue + } + + if (value !== undefined) { // These are qualified (something like `public=...`) when they aren't - // allowed to be, skip + // allowed to be, skip all instances of the malformed directive. + delete output[key] + invalidNoArgumentDirectives.add(key) continue } + output[key] = true + break + case 'no-store': output[key] = true break default: @@ -278,31 +489,79 @@ function parseCacheControlHeader (header) { return output } +/** + * @param {string | string[]} varyHeader Vary header from the server + * @returns {string[]} + */ +function splitVaryHeader (varyHeader) { + const values = Array.isArray(varyHeader) ? varyHeader : [varyHeader] + const output = [] + + for (let i = 0; i < values.length; i++) { + const parts = values[i].split(',') + for (let j = 0; j < parts.length; j++) { + output.push(parts[j]) + } + } + + return output +} + +/** + * @param {string | string[]} varyHeader Vary header from the server + * @returns {boolean} + */ +function hasVaryStar (varyHeader) { + const values = splitVaryHeader(varyHeader) + for (let i = 0; i < values.length; i++) { + if (trimOWS(values[i]).indexOf('*') !== -1) { + return true + } + } + + return false +} + /** * @param {string | string[]} varyHeader Vary header from the server * @param {Record} headers Request headers - * @returns {Record} + * @returns {Record | undefined} */ function parseVaryHeader (varyHeader, headers) { - if (typeof varyHeader === 'string' && varyHeader.includes('*')) { + if (hasVaryStar(varyHeader)) { return headers } const output = /** @type {Record} */ ({}) - const varyingHeaders = typeof varyHeader === 'string' - ? varyHeader.split(',') - : varyHeader + const varyingHeaders = splitVaryHeader(varyHeader) for (const header of varyingHeaders) { - const trimmedHeader = header.trim().toLowerCase() + const trimmedHeader = trimOWS(header).toLowerCase() + + if (trimmedHeader.length === 0) { + continue + } - output[trimmedHeader] = headers[trimmedHeader] ?? null + if (!isValidHTTPToken(trimmedHeader)) { + return undefined + } + + const headerValue = headers[trimmedHeader] + output[trimmedHeader] = Array.isArray(headerValue) ? headerValue.slice() : headerValue ?? null } return output } +/** + * @param {string | string[]} varyHeader Vary header from the server + * @returns {boolean} + */ +function isInvalidOrWildcardVaryHeader (varyHeader) { + return hasVaryStar(varyHeader) || parseVaryHeader(varyHeader, {}) === undefined +} + /** * Note: this deviates from the spec a little. Empty etags ("", W/"") are valid, * however, including them in cached resposnes serves little to no purpose. @@ -366,7 +625,7 @@ function assertCacheMethods (methods, name = 'CacheMethods') { } for (const method of methods) { - if (!safeHTTPMethods.includes(method)) { + if (!arrayIncludes(safeHTTPMethods, method)) { throw new TypeError(`element of ${name}-array needs to be one of following values: ${safeHTTPMethods.join(', ')}, got ${method}`) } } @@ -406,7 +665,10 @@ module.exports = { assertCacheKey, assertCacheValue, parseCacheControlHeader, + hasInvalidCacheControlDirective, parseVaryHeader, + hasVaryStar, + isInvalidOrWildcardVaryHeader, isEtagUsable, assertCacheMethods, assertCacheStore, diff --git a/deps/undici/src/lib/util/date.js b/deps/undici/src/lib/util/date.js index 99dd150ab38..a1862a07dc1 100644 --- a/deps/undici/src/lib/util/date.js +++ b/deps/undici/src/lib/util/date.js @@ -18,6 +18,26 @@ function parseHttpDate (date) { } } +function makeDate (year, monthIdx, day, hour, minute, second, weekday) { + const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second)) + + // Date.UTC treats years 0-99 as 1900-1999. Reset the full year so component + // checks below validate the HTTP date as written. + if (year >= 0 && year <= 99) { + result.setUTCFullYear(year) + } + + return result.getUTCFullYear() === year && + result.getUTCMonth() === monthIdx && + result.getUTCDate() === day && + result.getUTCHours() === hour && + result.getUTCMinutes() === minute && + result.getUTCSeconds() === second && + result.getUTCDay() === weekday + ? result + : undefined +} + /** * @see https://httpwg.org/specs/rfc9110.html#preferred.date.format * @@ -224,8 +244,7 @@ function parseImfDate (date) { second = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number } - const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second)) - return result.getUTCDay() === weekday ? result : undefined + return makeDate(year, monthIdx, day, hour, minute, second, weekday) } /** @@ -429,8 +448,7 @@ function parseAscTimeDate (date) { } const year = (yearDigit1 - 48) * 1000 + (yearDigit2 - 48) * 100 + (yearDigit3 - 48) * 10 + (yearDigit4 - 48) - const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second)) - return result.getUTCDay() === weekday ? result : undefined + return makeDate(year, monthIdx, day, hour, minute, second, weekday) } /** @@ -644,8 +662,7 @@ function parseRfc850Date (date) { second = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number } - const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second)) - return result.getUTCDay() === weekday ? result : undefined + return makeDate(year, monthIdx, day, hour, minute, second, weekday) } module.exports = { diff --git a/deps/undici/src/lib/web/cookies/util.js b/deps/undici/src/lib/web/cookies/util.js index 254f5419e90..ca408e153ab 100644 --- a/deps/undici/src/lib/web/cookies/util.js +++ b/deps/undici/src/lib/web/cookies/util.js @@ -105,7 +105,7 @@ function validateCookiePath (path) { if ( code < 0x20 || // exclude CTLs (0-31) - code === 0x7F || // DEL + code > 0x7E || // exclude DEL and non-ascii code === 0x3B // ; ) { throw new Error('Invalid cookie path') @@ -114,16 +114,80 @@ function validateCookiePath (path) { } /** - * I have no idea why these values aren't allowed to be honest, - * but Deno tests these. - Khafra + * ::= | + * + * ::= any one of the 52 alphabetic characters A through Z in + * upper case and a through z in lower case + * + * ::= any one of the ten digits 0 through 9r + * + * @see https://www.rfc-editor.org/rfc/rfc1034#section-3.5 + * @param {number} code + */ +function isLetterOrDigit (code) { + return ( + (code >= 0x30 && code <= 0x39) || // 0-9 + (code >= 0x41 && code <= 0x5A) || // A-Z + (code >= 0x61 && code <= 0x7A) // a-z + ) +} + +/** + * Validates a cookie domain against the "preferred name syntax". + * + * ::= | " " + * ::=