From c8421204b03117dfb4f920645d818b2e959e6463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Mon, 5 Dec 2022 12:40:47 +0000 Subject: [PATCH 01/97] deps: patch V8 to 10.8.168.21 Refs: https://github.com/v8/v8/compare/10.8.168.20...10.8.168.21 PR-URL: https://github.com/nodejs/node/pull/45749 Reviewed-By: Jiawen Geng Reviewed-By: Yagiz Nizipli --- deps/v8/include/v8-version.h | 2 +- deps/v8/src/ast/scopes.cc | 25 +++++------- deps/v8/src/ast/scopes.h | 75 +++++++++++++++--------------------- 3 files changed, 41 insertions(+), 61 deletions(-) diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index 027a13a3b239bc..1253f9ef0eecf3 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 10 #define V8_MINOR_VERSION 8 #define V8_BUILD_NUMBER 168 -#define V8_PATCH_LEVEL 20 +#define V8_PATCH_LEVEL 21 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/src/ast/scopes.cc b/deps/v8/src/ast/scopes.cc index 8d52d0792bcfc1..7bf3bcc7796d60 100644 --- a/deps/v8/src/ast/scopes.cc +++ b/deps/v8/src/ast/scopes.cc @@ -885,9 +885,8 @@ void DeclarationScope::AddLocal(Variable* var) { } void Scope::Snapshot::Reparent(DeclarationScope* new_parent) { - DCHECK(!IsCleared()); - DCHECK_EQ(new_parent, outer_scope_and_calls_eval_.GetPointer()->inner_scope_); - DCHECK_EQ(new_parent->outer_scope_, outer_scope_and_calls_eval_.GetPointer()); + DCHECK_EQ(new_parent, outer_scope_->inner_scope_); + DCHECK_EQ(new_parent->outer_scope_, outer_scope_); DCHECK_EQ(new_parent, new_parent->GetClosureScope()); DCHECK_NULL(new_parent->inner_scope_); DCHECK(new_parent->unresolved_list_.is_empty()); @@ -912,12 +911,11 @@ void Scope::Snapshot::Reparent(DeclarationScope* new_parent) { new_parent->sibling_ = top_inner_scope_; } - Scope* outer_scope = outer_scope_and_calls_eval_.GetPointer(); - new_parent->unresolved_list_.MoveTail(&outer_scope->unresolved_list_, + new_parent->unresolved_list_.MoveTail(&outer_scope_->unresolved_list_, top_unresolved_); // Move temporaries allocated for complex parameter initializers. - DeclarationScope* outer_closure = outer_scope->GetClosureScope(); + DeclarationScope* outer_closure = outer_scope_->GetClosureScope(); for (auto it = top_local_; it != outer_closure->locals()->end(); ++it) { Variable* local = *it; DCHECK_EQ(VariableMode::kTemporary, local->mode()); @@ -929,16 +927,10 @@ void Scope::Snapshot::Reparent(DeclarationScope* new_parent) { outer_closure->locals_.Rewind(top_local_); // Move eval calls since Snapshot's creation into new_parent. - if (outer_scope_and_calls_eval_->calls_eval_) { - new_parent->RecordDeclarationScopeEvalCall(); - new_parent->inner_scope_calls_eval_ = true; + if (outer_scope_->calls_eval_) { + new_parent->RecordEvalCall(); + declaration_scope_->sloppy_eval_can_extend_vars_ = false; } - - // We are in the arrow function case. The calls eval we may have recorded - // is intended for the inner scope and we should simply restore the - // original "calls eval" flag of the outer scope. - RestoreEvalFlag(); - Clear(); } void Scope::ReplaceOuterScope(Scope* outer) { @@ -2576,6 +2568,9 @@ void Scope::AllocateVariablesRecursively() { this->ForEach([](Scope* scope) -> Iteration { DCHECK(!scope->already_resolved_); if (WasLazilyParsed(scope)) return Iteration::kContinue; + if (scope->sloppy_eval_can_extend_vars_) { + scope->num_heap_slots_ = Context::MIN_CONTEXT_EXTENDED_SLOTS; + } DCHECK_EQ(scope->ContextHeaderLength(), scope->num_heap_slots_); // Allocate variables for this scope. diff --git a/deps/v8/src/ast/scopes.h b/deps/v8/src/ast/scopes.h index 32e16b80b261fb..3d062685643ad7 100644 --- a/deps/v8/src/ast/scopes.h +++ b/deps/v8/src/ast/scopes.h @@ -110,12 +110,6 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { class Snapshot final { public: - Snapshot() - : outer_scope_and_calls_eval_(nullptr, false), - top_unresolved_(), - top_local_() { - DCHECK(IsCleared()); - } inline explicit Snapshot(Scope* scope); // Disallow copy and move. @@ -123,45 +117,31 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { Snapshot(Snapshot&&) = delete; ~Snapshot() { - // If we're still active, there was no arrow function. In that case outer - // calls eval if it already called eval before this snapshot started, or - // if the code during the snapshot called eval. - if (!IsCleared() && outer_scope_and_calls_eval_.GetPayload()) { - RestoreEvalFlag(); + // Restore eval flags from before the scope was active. + if (sloppy_eval_can_extend_vars_) { + declaration_scope_->sloppy_eval_can_extend_vars_ = true; } - } - - void RestoreEvalFlag() { - if (outer_scope_and_calls_eval_.GetPayload()) { - // This recreates both calls_eval and sloppy_eval_can_extend_vars. - outer_scope_and_calls_eval_.GetPointer()->RecordEvalCall(); + if (calls_eval_) { + outer_scope_->calls_eval_ = true; } } void Reparent(DeclarationScope* new_parent); - bool IsCleared() const { - return outer_scope_and_calls_eval_.GetPointer() == nullptr; - } - - void Clear() { - outer_scope_and_calls_eval_.SetPointer(nullptr); -#ifdef DEBUG - outer_scope_and_calls_eval_.SetPayload(false); - top_inner_scope_ = nullptr; - top_local_ = base::ThreadedList::Iterator(); - top_unresolved_ = UnresolvedList::Iterator(); -#endif - } private: - // During tracking calls_eval caches whether the outer scope called eval. - // Upon move assignment we store whether the new inner scope calls eval into - // the move target calls_eval bit, and restore calls eval on the outer - // scope. - base::PointerWithPayload outer_scope_and_calls_eval_; + Scope* outer_scope_; + Scope* declaration_scope_; Scope* top_inner_scope_; UnresolvedList::Iterator top_unresolved_; base::ThreadedList::Iterator top_local_; + // While the scope is active, the scope caches the flag values for + // outer_scope_ / declaration_scope_ they can be used to know what happened + // while parsing the arrow head. If this turns out to be an arrow head, new + // values on the respective scopes will be cleared and moved to the inner + // scope. Otherwise the cached flags will be merged with the flags from the + // arrow head. + bool calls_eval_; + bool sloppy_eval_can_extend_vars_; }; enum class DeserializationMode { kIncludingVariables, kScopesOnly }; @@ -907,8 +887,8 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { void RecordDeclarationScopeEvalCall() { calls_eval_ = true; - // If this isn't a sloppy eval, we don't care about it. - if (language_mode() != LanguageMode::kSloppy) return; + // The caller already checked whether we're in sloppy mode. + CHECK(is_sloppy(language_mode())); // Sloppy eval in script scopes can only introduce global variables anyway, // so we don't care that it calls sloppy eval. @@ -942,7 +922,6 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { } sloppy_eval_can_extend_vars_ = true; - num_heap_slots_ = Context::MIN_CONTEXT_EXTENDED_SLOTS; } bool sloppy_eval_can_extend_vars() const { @@ -1367,7 +1346,9 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { void Scope::RecordEvalCall() { calls_eval_ = true; - GetDeclarationScope()->RecordDeclarationScopeEvalCall(); + if (is_sloppy(language_mode())) { + GetDeclarationScope()->RecordDeclarationScopeEvalCall(); + } RecordInnerScopeEvalCall(); // The eval contents might access "super" (if it's inside a function that // binds super). @@ -1380,14 +1361,18 @@ void Scope::RecordEvalCall() { } Scope::Snapshot::Snapshot(Scope* scope) - : outer_scope_and_calls_eval_(scope, scope->calls_eval_), + : outer_scope_(scope), + declaration_scope_(scope->GetDeclarationScope()), top_inner_scope_(scope->inner_scope_), top_unresolved_(scope->unresolved_list_.end()), - top_local_(scope->GetClosureScope()->locals_.end()) { - // Reset in order to record eval calls during this Snapshot's lifetime. - outer_scope_and_calls_eval_.GetPointer()->calls_eval_ = false; - outer_scope_and_calls_eval_.GetPointer()->sloppy_eval_can_extend_vars_ = - false; + top_local_(scope->GetClosureScope()->locals_.end()), + calls_eval_(outer_scope_->calls_eval_), + sloppy_eval_can_extend_vars_( + declaration_scope_->sloppy_eval_can_extend_vars_) { + // Reset in order to record (sloppy) eval calls during this Snapshot's + // lifetime. + outer_scope_->calls_eval_ = false; + declaration_scope_->sloppy_eval_can_extend_vars_ = false; } class ModuleScope final : public DeclarationScope { From c9cbd1d396dfcf4322ffbb52217ce4a24c5c5af6 Mon Sep 17 00:00:00 2001 From: Colin Ihrig Date: Thu, 24 Nov 2022 21:48:21 -0500 Subject: [PATCH 02/97] test_runner: remove stdout and stderr from error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CLI test runner parses the TAP output from child processes now and displays it. This commit removes a previous workaround for displaying child process stdout and stderr when tests failures occurred. PR-URL: https://github.com/nodejs/node/pull/45592 Reviewed-By: Michaël Zasso Reviewed-By: Moshe Atlow --- lib/internal/test_runner/runner.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/internal/test_runner/runner.js b/lib/internal/test_runner/runner.js index fde85a36325917..3f7134c6cd30cc 100644 --- a/lib/internal/test_runner/runner.js +++ b/lib/internal/test_runner/runner.js @@ -234,7 +234,6 @@ function runTestFile(path, root, inspectPort, filesWatcher) { runningProcesses.set(path, child); let err; - let stderr = ''; filesWatcher?.watchChildProcessModules(child, path); @@ -242,10 +241,6 @@ function runTestFile(path, root, inspectPort, filesWatcher) { err = error; }); - child.stderr.on('data', (data) => { - stderr += data; - }); - if (isUsingInspector()) { const rl = createInterface({ input: child.stderr }); rl.on('line', (line) => { @@ -266,7 +261,7 @@ function runTestFile(path, root, inspectPort, filesWatcher) { subtest.addToReport(ast); }); - const { 0: { 0: code, 1: signal }, 1: stdout } = await SafePromiseAll([ + const { 0: { 0: code, 1: signal } } = await SafePromiseAll([ once(child, 'exit', { signal: t.signal }), child.stdout.toArray({ signal: t.signal }), ]); @@ -279,8 +274,6 @@ function runTestFile(path, root, inspectPort, filesWatcher) { __proto__: null, exitCode: code, signal: signal, - stdout: ArrayPrototypeJoin(stdout, ''), - stderr, // The stack will not be useful since the failures came from tests // in a child process. stack: undefined, From 60c9ac5178a85ea24efb5fa871ea20426000351f Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Fri, 25 Nov 2022 06:07:40 -0500 Subject: [PATCH 03/97] deps: update nghttp2 to 1.51.0 PR-URL: https://github.com/nodejs/node/pull/45537 Reviewed-By: Rafael Gonzaga Reviewed-By: Matteo Collina --- deps/nghttp2/lib/CMakeLists.txt | 1 + deps/nghttp2/lib/Makefile.am | 2 + deps/nghttp2/lib/Makefile.in | 15 +- deps/nghttp2/lib/includes/Makefile.in | 3 +- deps/nghttp2/lib/includes/nghttp2/nghttp2.h | 249 +++++- .../nghttp2/lib/includes/nghttp2/nghttp2ver.h | 4 +- deps/nghttp2/lib/nghttp2_extpri.c | 35 + deps/nghttp2/lib/nghttp2_extpri.h | 65 ++ deps/nghttp2/lib/nghttp2_frame.c | 81 ++ deps/nghttp2/lib/nghttp2_frame.h | 45 + deps/nghttp2/lib/nghttp2_hd.c | 5 + deps/nghttp2/lib/nghttp2_hd.h | 1 + deps/nghttp2/lib/nghttp2_helper.c | 13 + deps/nghttp2/lib/nghttp2_http.c | 796 +++++++++++++++++- deps/nghttp2/lib/nghttp2_http.h | 51 ++ deps/nghttp2/lib/nghttp2_net.h | 12 +- deps/nghttp2/lib/nghttp2_option.c | 17 + deps/nghttp2/lib/nghttp2_option.h | 10 + deps/nghttp2/lib/nghttp2_outbound_item.c | 3 + deps/nghttp2/lib/nghttp2_pq.c | 3 +- deps/nghttp2/lib/nghttp2_pq.h | 8 +- deps/nghttp2/lib/nghttp2_session.c | 706 +++++++++++++++- deps/nghttp2/lib/nghttp2_session.h | 41 +- deps/nghttp2/lib/nghttp2_stream.c | 18 + deps/nghttp2/lib/nghttp2_stream.h | 24 +- deps/nghttp2/lib/nghttp2_submit.c | 82 +- deps/nghttp2/nghttp2.gyp | 1 + 27 files changed, 2190 insertions(+), 101 deletions(-) create mode 100644 deps/nghttp2/lib/nghttp2_extpri.c create mode 100644 deps/nghttp2/lib/nghttp2_extpri.h diff --git a/deps/nghttp2/lib/CMakeLists.txt b/deps/nghttp2/lib/CMakeLists.txt index 4dc2fcdbac6a60..fb24fa186f1114 100644 --- a/deps/nghttp2/lib/CMakeLists.txt +++ b/deps/nghttp2/lib/CMakeLists.txt @@ -23,6 +23,7 @@ set(NGHTTP2_SOURCES nghttp2_mem.c nghttp2_http.c nghttp2_rcbuf.c + nghttp2_extpri.c nghttp2_debug.c ) diff --git a/deps/nghttp2/lib/Makefile.am b/deps/nghttp2/lib/Makefile.am index 1e1f24843ac713..9a985bf76b28a8 100644 --- a/deps/nghttp2/lib/Makefile.am +++ b/deps/nghttp2/lib/Makefile.am @@ -50,6 +50,7 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ nghttp2_mem.c \ nghttp2_http.c \ nghttp2_rcbuf.c \ + nghttp2_extpri.c \ nghttp2_debug.c HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ @@ -66,6 +67,7 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ nghttp2_mem.h \ nghttp2_http.h \ nghttp2_rcbuf.h \ + nghttp2_extpri.h \ nghttp2_debug.h libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) diff --git a/deps/nghttp2/lib/Makefile.in b/deps/nghttp2/lib/Makefile.in index 5653774d5d6a1c..6b8722d2d9a9dc 100644 --- a/deps/nghttp2/lib/Makefile.in +++ b/deps/nghttp2/lib/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.16.4 from Makefile.am. +# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. @@ -162,7 +162,7 @@ am__objects_2 = nghttp2_pq.lo nghttp2_map.lo nghttp2_queue.lo \ nghttp2_hd_huffman.lo nghttp2_hd_huffman_data.lo \ nghttp2_version.lo nghttp2_priority_spec.lo nghttp2_option.lo \ nghttp2_callbacks.lo nghttp2_mem.lo nghttp2_http.lo \ - nghttp2_rcbuf.lo nghttp2_debug.lo + nghttp2_rcbuf.lo nghttp2_extpri.lo nghttp2_debug.lo am_libnghttp2_la_OBJECTS = $(am__objects_1) $(am__objects_2) libnghttp2_la_OBJECTS = $(am_libnghttp2_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) @@ -189,8 +189,9 @@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/nghttp2_buf.Plo \ ./$(DEPDIR)/nghttp2_callbacks.Plo \ - ./$(DEPDIR)/nghttp2_debug.Plo ./$(DEPDIR)/nghttp2_frame.Plo \ - ./$(DEPDIR)/nghttp2_hd.Plo ./$(DEPDIR)/nghttp2_hd_huffman.Plo \ + ./$(DEPDIR)/nghttp2_debug.Plo ./$(DEPDIR)/nghttp2_extpri.Plo \ + ./$(DEPDIR)/nghttp2_frame.Plo ./$(DEPDIR)/nghttp2_hd.Plo \ + ./$(DEPDIR)/nghttp2_hd_huffman.Plo \ ./$(DEPDIR)/nghttp2_hd_huffman_data.Plo \ ./$(DEPDIR)/nghttp2_helper.Plo ./$(DEPDIR)/nghttp2_http.Plo \ ./$(DEPDIR)/nghttp2_map.Plo ./$(DEPDIR)/nghttp2_mem.Plo \ @@ -336,6 +337,7 @@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ +FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX14 = @HAVE_CXX14@ INSTALL = @INSTALL@ @@ -523,6 +525,7 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ nghttp2_mem.c \ nghttp2_http.c \ nghttp2_rcbuf.c \ + nghttp2_extpri.c \ nghttp2_debug.c HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ @@ -539,6 +542,7 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ nghttp2_mem.h \ nghttp2_http.h \ nghttp2_rcbuf.h \ + nghttp2_extpri.h \ nghttp2_debug.h libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) @@ -628,6 +632,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_callbacks.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_debug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_extpri.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman.Plo@am__quote@ # am--include-marker @@ -909,6 +914,7 @@ distclean: distclean-recursive -rm -f ./$(DEPDIR)/nghttp2_buf.Plo -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo -rm -f ./$(DEPDIR)/nghttp2_debug.Plo + -rm -f ./$(DEPDIR)/nghttp2_extpri.Plo -rm -f ./$(DEPDIR)/nghttp2_frame.Plo -rm -f ./$(DEPDIR)/nghttp2_hd.Plo -rm -f ./$(DEPDIR)/nghttp2_hd_huffman.Plo @@ -976,6 +982,7 @@ maintainer-clean: maintainer-clean-recursive -rm -f ./$(DEPDIR)/nghttp2_buf.Plo -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo -rm -f ./$(DEPDIR)/nghttp2_debug.Plo + -rm -f ./$(DEPDIR)/nghttp2_extpri.Plo -rm -f ./$(DEPDIR)/nghttp2_frame.Plo -rm -f ./$(DEPDIR)/nghttp2_hd.Plo -rm -f ./$(DEPDIR)/nghttp2_hd_huffman.Plo diff --git a/deps/nghttp2/lib/includes/Makefile.in b/deps/nghttp2/lib/includes/Makefile.in index 327e523197e4ca..1943ca83222b77 100644 --- a/deps/nghttp2/lib/includes/Makefile.in +++ b/deps/nghttp2/lib/includes/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.16.4 from Makefile.am. +# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. @@ -245,6 +245,7 @@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ +FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX14 = @HAVE_CXX14@ INSTALL = @INSTALL@ diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h index 04321a652f145b..65077dd51613c1 100644 --- a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h +++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h @@ -634,7 +634,11 @@ typedef enum { * The ORIGIN frame, which is defined by `RFC 8336 * `_. */ - NGHTTP2_ORIGIN = 0x0c + NGHTTP2_ORIGIN = 0x0c, + /** + * The PRIORITY_UPDATE frame, which is defined by :rfc:`9218`. + */ + NGHTTP2_PRIORITY_UPDATE = 0x10 } nghttp2_frame_type; /** @@ -703,7 +707,11 @@ typedef enum { * SETTINGS_ENABLE_CONNECT_PROTOCOL * (`RFC 8441 `_) */ - NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08 + NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08, + /** + * SETTINGS_NO_RFC7540_PRIORITIES (:rfc:`9218`) + */ + NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES = 0x09 } nghttp2_settings_id; /* Note: If we add SETTINGS, update the capacity of NGHTTP2_INBOUND_NUM_IV as well */ @@ -1422,12 +1430,6 @@ typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, * respectively. The header name/value pairs are emitted via * :type:`nghttp2_on_header_callback`. * - * For HEADERS, PUSH_PROMISE and DATA frames, this callback may be - * called after stream is closed (see - * :type:`nghttp2_on_stream_close_callback`). The application should - * check that stream is still alive using its own stream management or - * :func:`nghttp2_session_get_stream_user_data()`. - * * Only HEADERS and DATA frame can signal the end of incoming data. * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the * |frame| is the last frame from the remote peer in this stream. @@ -2693,6 +2695,11 @@ nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, * This option prevents the library from retaining closed streams to * maintain the priority tree. If this option is set to nonzero, * applications can discard closed stream completely to save memory. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is submitted via `nghttp2_submit_settings()`, any + * closed streams are not retained regardless of this option. */ NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val); @@ -2719,6 +2726,36 @@ NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val); +/** + * @function + * + * This option, if set to nonzero, allows server to fallback to + * :rfc:`7540` priorities if SETTINGS_NO_RFC7540_PRIORITIES was not + * received from client, and server submitted + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * = 1 via `nghttp2_submit_settings()`. Most of the advanced + * functionality for RFC 7540 priorities are still disabled. This + * fallback only enables the minimal feature set of RFC 7540 + * priorities to deal with priority signaling from client. + * + * Client session ignores this option. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_server_fallback_rfc7540_priorities(nghttp2_option *option, + int val); + +/** + * @function + * + * This option, if set to nonzero, turns off RFC 9113 leading and + * trailing white spaces validation against HTTP field value. Some + * important fields, such as HTTP/2 pseudo header fields, are + * validated more strictly and this option does not apply to them. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation( + nghttp2_option *option, int val); + /** * @function * @@ -3589,6 +3626,11 @@ NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, * found, we use default priority instead of given |pri_spec|. That * is make stream depend on root stream with weight 16. * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is submitted via `nghttp2_submit_settings()`, this + * function does nothing and returns 0. + * * This function returns 0 if it succeeds, or one of the following * negative error codes: * @@ -3632,6 +3674,11 @@ nghttp2_session_change_stream_priority(nghttp2_session *session, * found, we use default priority instead of given |pri_spec|. That * is make stream depend on root stream with weight 16. * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is submitted via `nghttp2_submit_settings()`, this + * function does nothing and returns 0. + * * This function returns 0 if it succeeds, or one of the following * negative error codes: * @@ -3837,6 +3884,11 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes * :macro:`NGHTTP2_MAX_WEIGHT`. * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is received by a remote endpoint, |pri_spec| is + * ignored, and treated as if ``NULL`` is specified. + * * The |nva| is an array of name/value pair :type:`nghttp2_nv` with * |nvlen| elements. The application is responsible to include * required pseudo-header fields (header field whose name starts with @@ -4057,6 +4109,11 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is received by a remote endpoint, |pri_spec| is + * ignored, and treated as if ``NULL`` is specified. + * * The |nva| is an array of name/value pair :type:`nghttp2_nv` with * |nvlen| elements. The application is responsible to include * required pseudo-header fields (header field whose name starts with @@ -4184,6 +4241,11 @@ NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes * :macro:`NGHTTP2_MAX_WEIGHT`. * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is received by a remote endpoint, this function does + * nothing and returns 0. + * * This function returns 0 if it succeeds, or one of the following * negative error codes: * @@ -4198,6 +4260,61 @@ nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec); +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_DEFAULT_URGENCY` is the default urgency + * level for :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_DEFAULT_URGENCY 3 + +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_URGENCY_HIGH` is the highest urgency level + * for :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_URGENCY_HIGH 0 + +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW` is the lowest urgency level for + * :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_URGENCY_LOW 7 + +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_URGENCY_LEVELS` is the number of urgency + * levels for :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_URGENCY_LEVELS (NGHTTP2_EXTPRI_URGENCY_LOW + 1) + +/** + * @struct + * + * :type:`nghttp2_extpri` is :rfc:`9218` extensible priorities + * specification for a stream. + */ +typedef struct nghttp2_extpri { + /** + * :member:`urgency` is the urgency of a stream, it must be in + * [:macro:`NGHTTP2_EXTPRI_URGENCY_HIGH`, + * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`], inclusive, and 0 is the + * highest urgency. + */ + uint32_t urgency; + /** + * :member:`inc` indicates that a content can be processed + * incrementally or not. If inc is 0, it cannot be processed + * incrementally. If inc is 1, it can be processed incrementally. + * Other value is not permitted. + */ + int inc; +} nghttp2_extpri; + /** * @function * @@ -4722,6 +4839,108 @@ NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session, const nghttp2_origin_entry *ov, size_t nov); +/** + * @struct + * + * The payload of PRIORITY_UPDATE frame. PRIORITY_UPDATE frame is a + * non-critical extension to HTTP/2. If this frame is received, and + * `nghttp2_option_set_user_recv_extension_type()` is not set, and + * `nghttp2_option_set_builtin_recv_extension_type()` is set for + * :enum:`nghttp2_frame_type.NGHTTP2_PRIORITY_UPDATE`, + * ``nghttp2_extension.payload`` will point to this struct. + * + * It has the following members: + */ +typedef struct { + /** + * The stream ID of the stream whose priority is updated. + */ + int32_t stream_id; + /** + * The pointer to Priority field value. It is not necessarily + * NULL-terminated. + */ + uint8_t *field_value; + /** + * The length of the :member:`field_value`. + */ + size_t field_value_len; +} nghttp2_ext_priority_update; + +/** + * @function + * + * Submits PRIORITY_UPDATE frame. + * + * PRIORITY_UPDATE frame is a non-critical extension to HTTP/2, and + * defined in :rfc:`9218#section-7.1`. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |stream_id| is the ID of stream which is prioritized. The + * |field_value| points to the Priority field value. The + * |field_value_len| is the length of the Priority field value. + * + * If this function is called by server, + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` is returned. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 0 is received by a remote endpoint (or it is omitted), + * this function does nothing and returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The function is called from server side session + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |field_value_len| is larger than 16380; or |stream_id| is + * 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_priority_update(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + const uint8_t *field_value, + size_t field_value_len); + +/** + * @function + * + * Changes the priority of the existing stream denoted by |stream_id|. + * The new priority is |extpri|. This function is meant to be used by + * server for :rfc:`9218` extensible prioritization scheme. + * + * If |session| is initialized as client, this function returns + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. For client, use + * `nghttp2_submit_priority_update()` instead. + * + * If :member:`extpri->urgency ` is out of + * bound, it is set to :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`. + * + * If |ignore_client_signal| is nonzero, server starts to ignore + * client priority signals for this stream. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is not submitted via `nghttp2_submit_settings()`, + * this function does nothing and returns 0. + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The |session| is initialized as client. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * |stream_id| is zero; or a stream denoted by |stream_id| is not + * found. + */ +NGHTTP2_EXTERN int nghttp2_session_change_extpri_stream_priority( + nghttp2_session *session, int32_t stream_id, const nghttp2_extpri *extpri, + int ignore_client_signal); + /** * @function * @@ -4833,9 +5052,23 @@ NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); * Returns nonzero if HTTP header field value |value| of length |len| * is valid according to * http://tools.ietf.org/html/rfc7230#section-3.2 + * + * This function is considered obsolete, and application should + * consider to use `nghttp2_check_header_value_rfc9113()` instead. */ NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); +/** + * @function + * + * Returns nonzero if HTTP header field value |value| of length |len| + * is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2, plus + * https://datatracker.ietf.org/doc/html/rfc9113#section-8.2.1 + */ +NGHTTP2_EXTERN int nghttp2_check_header_value_rfc9113(const uint8_t *value, + size_t len); + /** * @function * diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h index c6082518c7b5fa..6fe61752afb9d5 100644 --- a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h +++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h @@ -29,7 +29,7 @@ * @macro * Version number of the nghttp2 library release */ -#define NGHTTP2_VERSION "1.47.0" +#define NGHTTP2_VERSION "1.51.0" /** * @macro @@ -37,6 +37,6 @@ * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ -#define NGHTTP2_VERSION_NUM 0x012f00 +#define NGHTTP2_VERSION_NUM 0x013300 #endif /* NGHTTP2VER_H */ diff --git a/deps/nghttp2/lib/nghttp2_extpri.c b/deps/nghttp2/lib/nghttp2_extpri.c new file mode 100644 index 00000000000000..3fd9b78163e323 --- /dev/null +++ b/deps/nghttp2/lib/nghttp2_extpri.c @@ -0,0 +1,35 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_extpri.h" + +uint8_t nghttp2_extpri_to_uint8(const nghttp2_extpri *extpri) { + return (uint8_t)((uint32_t)extpri->inc << 7 | extpri->urgency); +} + +void nghttp2_extpri_from_uint8(nghttp2_extpri *extpri, uint8_t u8extpri) { + extpri->urgency = nghttp2_extpri_uint8_urgency(u8extpri); + extpri->inc = nghttp2_extpri_uint8_inc(u8extpri); +} diff --git a/deps/nghttp2/lib/nghttp2_extpri.h b/deps/nghttp2/lib/nghttp2_extpri.h new file mode 100644 index 00000000000000..23c6ddc0c0539d --- /dev/null +++ b/deps/nghttp2/lib/nghttp2_extpri.h @@ -0,0 +1,65 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_EXTPRI_H +#define NGHTTP2_EXTPRI_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * NGHTTP2_EXTPRI_INC_MASK is a bit mask to retrieve incremental bit + * from a value produced by nghttp2_extpri_to_uint8. + */ +#define NGHTTP2_EXTPRI_INC_MASK (1 << 7) + +/* + * nghttp2_extpri_to_uint8 encodes |pri| into uint8_t variable. + */ +uint8_t nghttp2_extpri_to_uint8(const nghttp2_extpri *extpri); + +/* + * nghttp2_extpri_from_uint8 decodes |u8extpri|, which is produced by + * nghttp2_extpri_to_uint8, intto |extpri|. + */ +void nghttp2_extpri_from_uint8(nghttp2_extpri *extpri, uint8_t u8extpri); + +/* + * nghttp2_extpri_uint8_urgency extracts urgency from |PRI| which is + * supposed to be constructed by nghttp2_extpri_to_uint8. + */ +#define nghttp2_extpri_uint8_urgency(PRI) \ + ((uint32_t)((PRI) & ~NGHTTP2_EXTPRI_INC_MASK)) + +/* + * nghttp2_extpri_uint8_inc extracts inc from |PRI| which is supposed to + * be constructed by nghttp2_extpri_to_uint8. + */ +#define nghttp2_extpri_uint8_inc(PRI) (((PRI)&NGHTTP2_EXTPRI_INC_MASK) != 0) + +#endif /* NGHTTP2_EXTPRI_H */ diff --git a/deps/nghttp2/lib/nghttp2_frame.c b/deps/nghttp2/lib/nghttp2_frame.c index 3648b2389d7cbf..35072c15fc18e6 100644 --- a/deps/nghttp2/lib/nghttp2_frame.c +++ b/deps/nghttp2/lib/nghttp2_frame.c @@ -253,6 +253,31 @@ void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) { nghttp2_mem_free(mem, origin->ov); } +void nghttp2_frame_priority_update_init(nghttp2_extension *frame, + int32_t stream_id, uint8_t *field_value, + size_t field_value_len) { + nghttp2_ext_priority_update *priority_update; + + nghttp2_frame_hd_init(&frame->hd, 4 + field_value_len, + NGHTTP2_PRIORITY_UPDATE, NGHTTP2_FLAG_NONE, 0); + + priority_update = frame->payload; + priority_update->stream_id = stream_id; + priority_update->field_value = field_value; + priority_update->field_value_len = field_value_len; +} + +void nghttp2_frame_priority_update_free(nghttp2_extension *frame, + nghttp2_mem *mem) { + nghttp2_ext_priority_update *priority_update; + + priority_update = frame->payload; + if (priority_update == NULL) { + return; + } + nghttp2_mem_free(mem, priority_update->field_value); +} + size_t nghttp2_frame_priority_len(uint8_t flags) { if (flags & NGHTTP2_FLAG_PRIORITY) { return NGHTTP2_PRIORITY_SPECLEN; @@ -876,6 +901,57 @@ int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, return 0; } +int nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs, + nghttp2_extension *frame) { + int rv; + nghttp2_buf *buf; + nghttp2_ext_priority_update *priority_update; + + /* This is required with --disable-assert. */ + (void)rv; + + priority_update = frame->payload; + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 4 + priority_update->field_value_len); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, (uint32_t)priority_update->stream_id); + buf->last += 4; + + rv = nghttp2_bufs_add(bufs, priority_update->field_value, + priority_update->field_value_len); + + assert(rv == 0); + + return 0; +} + +void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame, + uint8_t *payload, + size_t payloadlen) { + nghttp2_ext_priority_update *priority_update; + + assert(payloadlen >= 4); + + priority_update = frame->payload; + + priority_update->stream_id = + nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + + if (payloadlen > 4) { + priority_update->field_value = payload + 4; + priority_update->field_value_len = payloadlen - 4; + } else { + priority_update->field_value = NULL; + priority_update->field_value_len = 0; + } +} + nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, size_t niv, nghttp2_mem *mem) { nghttp2_settings_entry *iv_copy; @@ -1071,6 +1147,11 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) { return 0; } break; + case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: + if (iv[i].value != 0 && iv[i].value != 1) { + return 0; + } + break; } } return 1; diff --git a/deps/nghttp2/lib/nghttp2_frame.h b/deps/nghttp2/lib/nghttp2_frame.h index 3859926ebbcbac..5f6152b74587ae 100644 --- a/deps/nghttp2/lib/nghttp2_frame.h +++ b/deps/nghttp2/lib/nghttp2_frame.h @@ -73,6 +73,7 @@ typedef union { nghttp2_ext_altsvc altsvc; nghttp2_ext_origin origin; + nghttp2_ext_priority_update priority_update; } nghttp2_ext_frame_payload; void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd); @@ -423,6 +424,31 @@ int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *ext); int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, const uint8_t *payload, size_t payloadlen, nghttp2_mem *mem); + +/* + * Packs PRIORITY_UPDATE frame |frame| in wire frame format and store + * it in |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs, + nghttp2_extension *ext); + +/* + * Unpacks PRIORITY_UPDATE wire format into |frame|. The |payload| of + * |payloadlen| bytes contains frame payload. This function assumes + * that frame->payload points to the nghttp2_ext_priority_update + * object. + * + * This function always succeeds and returns 0. + */ +void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame, + uint8_t *payload, + size_t payloadlen); + /* * Initializes HEADERS frame |frame| with given values. |frame| takes * ownership of |nva|, so caller must not free it. If |stream_id| is @@ -538,6 +564,25 @@ void nghttp2_frame_origin_init(nghttp2_extension *frame, */ void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem); +/* + * Initializes PRIORITY_UPDATE frame |frame| with given values. This + * function assumes that frame->payload points to + * nghttp2_ext_priority_update object. On success, this function + * takes ownership of |field_value|, so caller must not free it. + */ +void nghttp2_frame_priority_update_init(nghttp2_extension *frame, + int32_t stream_id, uint8_t *field_value, + size_t field_value_len); + +/* + * Frees up resources under |frame|. This function does not free + * nghttp2_ext_priority_update object pointed by frame->payload. This + * function only frees field_value pointed by + * nghttp2_ext_priority_update.field_value. + */ +void nghttp2_frame_priority_update_free(nghttp2_extension *frame, + nghttp2_mem *mem); + /* * Returns the number of padding bytes after payload. The total * padding length is given in the |padlen|. The returned value does diff --git a/deps/nghttp2/lib/nghttp2_hd.c b/deps/nghttp2/lib/nghttp2_hd.c index 30ee9b88920c0a..8a2bda64c1fe46 100644 --- a/deps/nghttp2/lib/nghttp2_hd.c +++ b/deps/nghttp2/lib/nghttp2_hd.c @@ -269,6 +269,11 @@ static int32_t lookup_token(const uint8_t *name, size_t namelen) { return NGHTTP2_TOKEN_LOCATION; } break; + case 'y': + if (memeq("priorit", name, 7)) { + return NGHTTP2_TOKEN_PRIORITY; + } + break; } break; case 9: diff --git a/deps/nghttp2/lib/nghttp2_hd.h b/deps/nghttp2/lib/nghttp2_hd.h index 267402881f4258..6de0052aaea6cd 100644 --- a/deps/nghttp2/lib/nghttp2_hd.h +++ b/deps/nghttp2/lib/nghttp2_hd.h @@ -112,6 +112,7 @@ typedef enum { NGHTTP2_TOKEN_PROXY_CONNECTION, NGHTTP2_TOKEN_UPGRADE, NGHTTP2_TOKEN__PROTOCOL, + NGHTTP2_TOKEN_PRIORITY, } nghttp2_token; struct nghttp2_hd_entry; diff --git a/deps/nghttp2/lib/nghttp2_helper.c b/deps/nghttp2/lib/nghttp2_helper.c index 588e269c3449fd..93dd4754b7f304 100644 --- a/deps/nghttp2/lib/nghttp2_helper.c +++ b/deps/nghttp2/lib/nghttp2_helper.c @@ -507,6 +507,19 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len) { return 1; } +int nghttp2_check_header_value_rfc9113(const uint8_t *value, size_t len) { + if (len == 0) { + return 1; + } + + if (*value == ' ' || *value == '\t' || *(value + len - 1) == ' ' || + *(value + len - 1) == '\t') { + return 0; + } + + return nghttp2_check_header_value(value, len); +} + /* Generated by genmethodchartbl.py */ static char VALID_METHOD_CHARS[] = { 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, diff --git a/deps/nghttp2/lib/nghttp2_http.c b/deps/nghttp2/lib/nghttp2_http.c index a2bcd2c0a19333..83e5e6685f86d8 100644 --- a/deps/nghttp2/lib/nghttp2_http.c +++ b/deps/nghttp2/lib/nghttp2_http.c @@ -30,6 +30,7 @@ #include "nghttp2_hd.h" #include "nghttp2_helper.h" +#include "nghttp2_extpri.h" static uint8_t downcase(uint8_t c) { return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; @@ -72,25 +73,12 @@ static int64_t parse_uint(const uint8_t *s, size_t len) { return n; } -static int lws(const uint8_t *s, size_t n) { - size_t i; - for (i = 0; i < n; ++i) { - if (s[i] != ' ' && s[i] != '\t') { - return 0; - } - } - return 1; -} - static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv, - int flag) { - if (stream->http_flags & flag) { - return 0; - } - if (lws(nv->value->base, nv->value->len)) { + uint32_t flag) { + if ((stream->http_flags & flag) || nv->value->len == 0) { return 0; } - stream->http_flags = (uint16_t)(stream->http_flags | flag); + stream->http_flags = stream->http_flags | flag; return 1; } @@ -114,6 +102,8 @@ static int check_path(nghttp2_stream *stream) { static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, int trailer, int connect_protocol) { + nghttp2_extpri extpri; + if (nv->name->base[0] == ':') { if (trailer || (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { @@ -212,6 +202,23 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, return NGHTTP2_ERR_HTTP_HEADER; } break; + case NGHTTP2_TOKEN_PRIORITY: + if (!trailer && + /* Do not parse the header field in PUSH_PROMISE. */ + (stream->stream_id & 1) && + (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) && + !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) { + nghttp2_extpri_from_uint8(&extpri, stream->http_extpri); + if (nghttp2_http_parse_priority(&extpri, nv->value->base, + nv->value->len) == 0) { + stream->http_extpri = nghttp2_extpri_to_uint8(&extpri); + stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY; + } else { + stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY; + stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY; + } + } + break; default: if (nv->name->base[0] == ':') { return NGHTTP2_ERR_HTTP_HEADER; @@ -329,6 +336,16 @@ static int check_scheme(const uint8_t *value, size_t len) { return 1; } +static int lws(const uint8_t *s, size_t n) { + size_t i; + for (i = 0; i < n; ++i) { + if (s[i] != ' ' && s[i] != '\t') { + return 0; + } + } + return 1; +} + int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, nghttp2_frame *frame, nghttp2_hd_nv *nv, int trailer) { @@ -369,13 +386,37 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, break; case NGHTTP2_TOKEN__AUTHORITY: case NGHTTP2_TOKEN_HOST: - rv = nghttp2_check_authority(nv->value->base, nv->value->len); + if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { + rv = nghttp2_check_authority(nv->value->base, nv->value->len); + } else if ( + stream->flags & + NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) { + rv = nghttp2_check_header_value(nv->value->base, nv->value->len); + } else { + rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len); + } break; case NGHTTP2_TOKEN__SCHEME: rv = check_scheme(nv->value->base, nv->value->len); break; + case NGHTTP2_TOKEN__PROTOCOL: + /* Check the value consists of just white spaces, which was done + in check_pseudo_header before + nghttp2_check_header_value_rfc9113 has been introduced. */ + if ((stream->flags & + NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) && + lws(nv->value->base, nv->value->len)) { + rv = 0; + break; + } + /* fall through */ default: - rv = nghttp2_check_header_value(nv->value->base, nv->value->len); + if (stream->flags & + NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) { + rv = nghttp2_check_header_value(nv->value->base, nv->value->len); + } else { + rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len); + } } if (rv == 0) { @@ -443,16 +484,15 @@ int nghttp2_http_on_response_headers(nghttp2_stream *stream) { if (stream->status_code / 100 == 1) { /* non-final response */ - stream->http_flags = - (uint16_t)((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) | - NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) | + NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; stream->content_length = -1; stream->status_code = -1; return 0; } stream->http_flags = - (uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + stream->http_flags & (uint32_t)~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; if (!expect_response_body(stream)) { stream->content_length = 0; @@ -537,3 +577,715 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream, return; } } + +/* Generated by genchartbl.py */ +static const int SF_KEY_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */, + 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 0 /* + */, 0 /* , */, + 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */, + 0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */, + 0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, + 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */, + 0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */, + 0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */, + 1 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */, + 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static ssize_t sf_parse_key(const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; + + if ((*p < 'a' || 'z' < *p) && *p != '*') { + return -1; + } + + for (; p != end && SF_KEY_CHARS[*p]; ++p) + ; + + return p - begin; +} + +static ssize_t sf_parse_integer_or_decimal(nghttp2_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + int sign = 1; + int64_t value = 0; + int type = NGHTTP2_SF_VALUE_TYPE_INTEGER; + size_t len = 0; + size_t fpos = 0; + size_t i; + + if (*p == '-') { + if (++p == end) { + return -1; + } + + sign = -1; + } + + if (*p < '0' || '9' < *p) { + return -1; + } + + for (; p != end; ++p) { + switch (*p) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + value *= 10; + value += *p - '0'; + + if (++len > 15) { + return -1; + } + + break; + case '.': + if (type != NGHTTP2_SF_VALUE_TYPE_INTEGER) { + goto fin; + } + + if (len > 12) { + return -1; + } + fpos = len; + type = NGHTTP2_SF_VALUE_TYPE_DECIMAL; + + break; + default: + goto fin; + }; + } + +fin: + switch (type) { + case NGHTTP2_SF_VALUE_TYPE_INTEGER: + if (dest) { + dest->type = (uint8_t)type; + dest->i = value * sign; + } + + return p - begin; + case NGHTTP2_SF_VALUE_TYPE_DECIMAL: + if (fpos == len || len - fpos > 3) { + return -1; + } + + if (dest) { + dest->type = (uint8_t)type; + dest->d = (double)value; + for (i = len - fpos; i > 0; --i) { + dest->d /= (double)10; + } + dest->d *= sign; + } + + return p - begin; + default: + assert(0); + abort(); + } +} + +/* Generated by genchartbl.py */ +static const int SF_DQUOTE_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 0 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */, + 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 1 /* [ */, 0 /* \ */, 1 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */, + 1 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static ssize_t sf_parse_string(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + + if (*p++ != '"') { + return -1; + } + + for (; p != end; ++p) { + switch (*p) { + case '\\': + if (++p == end) { + return -1; + } + + switch (*p) { + case '"': + case '\\': + break; + default: + return -1; + } + + break; + case '"': + if (dest) { + dest->type = NGHTTP2_SF_VALUE_TYPE_STRING; + dest->s.base = begin + 1; + dest->s.len = (size_t)(p - dest->s.base); + } + + ++p; + + return p - begin; + default: + if (!SF_DQUOTE_CHARS[*p]) { + return -1; + } + } + } + + return -1; +} + +/* Generated by genchartbl.py */ +static const int SF_TOKEN_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */, + 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */, + 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static ssize_t sf_parse_token(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + + if ((*p < 'A' || 'Z' < *p) && (*p < 'a' || 'z' < *p) && *p != '*') { + return -1; + } + + for (; p != end && SF_TOKEN_CHARS[*p]; ++p) + ; + + if (dest) { + dest->type = NGHTTP2_SF_VALUE_TYPE_TOKEN; + dest->s.base = begin; + dest->s.len = (size_t)(p - begin); + } + + return p - begin; +} + +/* Generated by genchartbl.py */ +static const int SF_BYTESEQ_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */, + 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */, + 0 /* ( */, 0 /* ) */, 0 /* * */, 1 /* + */, 0 /* , */, + 0 /* - */, 0 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */, + 0 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */, + 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static ssize_t sf_parse_byteseq(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + + if (*p++ != ':') { + return -1; + } + + for (; p != end; ++p) { + switch (*p) { + case ':': + if (dest) { + dest->type = NGHTTP2_SF_VALUE_TYPE_BYTESEQ; + dest->s.base = begin + 1; + dest->s.len = (size_t)(p - dest->s.base); + } + + ++p; + + return p - begin; + default: + if (!SF_BYTESEQ_CHARS[*p]) { + return -1; + } + } + } + + return -1; +} + +static ssize_t sf_parse_boolean(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + int b; + + if (*p++ != '?') { + return -1; + } + + if (p == end) { + return -1; + } + + switch (*p++) { + case '0': + b = 0; + break; + case '1': + b = 1; + break; + default: + return -1; + } + + if (dest) { + dest->type = NGHTTP2_SF_VALUE_TYPE_BOOLEAN; + dest->b = b; + } + + return p - begin; +} + +static ssize_t sf_parse_bare_item(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + switch (*begin) { + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return sf_parse_integer_or_decimal(dest, begin, end); + case '"': + return sf_parse_string(dest, begin, end); + case '*': + return sf_parse_token(dest, begin, end); + case ':': + return sf_parse_byteseq(dest, begin, end); + case '?': + return sf_parse_boolean(dest, begin, end); + default: + if (('A' <= *begin && *begin <= 'Z') || ('a' <= *begin && *begin <= 'z')) { + return sf_parse_token(dest, begin, end); + } + return -1; + } +} + +#define sf_discard_sp_end_err(BEGIN, END, ERR) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + return (ERR); \ + } \ + if (*(BEGIN) != ' ') { \ + break; \ + } \ + } + +static ssize_t sf_parse_params(const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; + ssize_t slen; + + for (; p != end && *p == ';';) { + ++p; + + sf_discard_sp_end_err(p, end, -1); + + slen = sf_parse_key(p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + if (p == end || *p != '=') { + /* Boolean true */ + } else if (++p == end) { + return -1; + } else { + slen = sf_parse_bare_item(NULL, p, end); + if (slen < 0) { + return -1; + } + + p += slen; + } + } + + return p - begin; +} + +static ssize_t sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + ssize_t slen; + + slen = sf_parse_bare_item(dest, p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + return p - begin; +} + +ssize_t nghttp2_sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + return sf_parse_item(dest, begin, end); +} + +static ssize_t sf_parse_inner_list(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + ssize_t slen; + + if (*p++ != '(') { + return -1; + } + + for (;;) { + sf_discard_sp_end_err(p, end, -1); + + if (*p == ')') { + ++p; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + if (dest) { + dest->type = NGHTTP2_SF_VALUE_TYPE_INNER_LIST; + } + + return p - begin; + } + + slen = sf_parse_item(NULL, p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + if (p == end || (*p != ' ' && *p != ')')) { + return -1; + } + } +} + +ssize_t nghttp2_sf_parse_inner_list(nghttp2_sf_value *dest, + const uint8_t *begin, const uint8_t *end) { + return sf_parse_inner_list(dest, begin, end); +} + +static ssize_t sf_parse_item_or_inner_list(nghttp2_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + if (*begin == '(') { + return sf_parse_inner_list(dest, begin, end); + } + + return sf_parse_item(dest, begin, end); +} + +#define sf_discard_ows(BEGIN, END) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + goto fin; \ + } \ + if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \ + break; \ + } \ + } + +#define sf_discard_ows_end_err(BEGIN, END, ERR) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + return (ERR); \ + } \ + if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \ + break; \ + } \ + } + +int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value, + size_t valuelen) { + const uint8_t *p = value, *end = value + valuelen; + ssize_t slen; + nghttp2_sf_value val; + nghttp2_extpri pri = *dest; + const uint8_t *key; + size_t keylen; + + for (; p != end && *p == ' '; ++p) + ; + + for (; p != end;) { + slen = sf_parse_key(p, end); + if (slen < 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + key = p; + keylen = (size_t)slen; + + p += slen; + + if (p == end || *p != '=') { + /* Boolean true */ + val.type = NGHTTP2_SF_VALUE_TYPE_BOOLEAN; + val.b = 1; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } else if (++p == end) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } else { + slen = sf_parse_item_or_inner_list(&val, p, end); + if (slen < 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } + + p += slen; + + if (keylen == 1) { + switch (key[0]) { + case 'i': + if (val.type != NGHTTP2_SF_VALUE_TYPE_BOOLEAN) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + pri.inc = val.b; + + break; + case 'u': + if (val.type != NGHTTP2_SF_VALUE_TYPE_INTEGER || + val.i < NGHTTP2_EXTPRI_URGENCY_HIGH || + NGHTTP2_EXTPRI_URGENCY_LOW < val.i) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + pri.urgency = (uint32_t)val.i; + + break; + } + } + + sf_discard_ows(p, end); + + if (*p++ != ',') { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + sf_discard_ows_end_err(p, end, NGHTTP2_ERR_INVALID_ARGUMENT); + } + +fin: + *dest = pri; + + return 0; +} diff --git a/deps/nghttp2/lib/nghttp2_http.h b/deps/nghttp2/lib/nghttp2_http.h index dd057cdb60757f..0c3a78eeefab79 100644 --- a/deps/nghttp2/lib/nghttp2_http.h +++ b/deps/nghttp2/lib/nghttp2_http.h @@ -94,4 +94,55 @@ int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n); void nghttp2_http_record_request_method(nghttp2_stream *stream, nghttp2_frame *frame); +/* + * RFC 8941 Structured Field Values. + */ +typedef enum nghttp2_sf_value_type { + NGHTTP2_SF_VALUE_TYPE_BOOLEAN, + NGHTTP2_SF_VALUE_TYPE_INTEGER, + NGHTTP2_SF_VALUE_TYPE_DECIMAL, + NGHTTP2_SF_VALUE_TYPE_STRING, + NGHTTP2_SF_VALUE_TYPE_TOKEN, + NGHTTP2_SF_VALUE_TYPE_BYTESEQ, + NGHTTP2_SF_VALUE_TYPE_INNER_LIST, +} nghttp2_sf_value_type; + +/* + * nghttp2_sf_value stores Structured Field Values item. For Inner + * List, only type is set to NGHTTP2_SF_VALUE_TYPE_INNER_LIST. + */ +typedef struct nghttp2_sf_value { + uint8_t type; + union { + int b; + int64_t i; + double d; + struct { + const uint8_t *base; + size_t len; + } s; + }; +} nghttp2_sf_value; + +/* + * nghttp2_sf_parse_item parses the input sequence [|begin|, |end|) + * and stores the parsed an Item in |dest|. It returns the number of + * bytes consumed if it succeeds, or -1. This function is declared + * here for unit tests. + */ +ssize_t nghttp2_sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end); + +/* + * nghttp2_sf_parse_inner_list parses the input sequence [|begin|, |end|) + * and stores the parsed an Inner List in |dest|. It returns the number of + * bytes consumed if it succeeds, or -1. This function is declared + * here for unit tests. + */ +ssize_t nghttp2_sf_parse_inner_list(nghttp2_sf_value *dest, + const uint8_t *begin, const uint8_t *end); + +int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value, + size_t valuelen); + #endif /* NGHTTP2_HTTP_H */ diff --git a/deps/nghttp2/lib/nghttp2_net.h b/deps/nghttp2/lib/nghttp2_net.h index 582099b93dc0b8..521f98143e4765 100644 --- a/deps/nghttp2/lib/nghttp2_net.h +++ b/deps/nghttp2/lib/nghttp2_net.h @@ -53,7 +53,7 @@ STIN uint32_t htonl(uint32_t hostlong) { uint32_t res; unsigned char *p = (unsigned char *)&res; - *p++ = hostlong >> 24; + *p++ = (unsigned char)(hostlong >> 24); *p++ = (hostlong >> 16) & 0xffu; *p++ = (hostlong >> 8) & 0xffu; *p = hostlong & 0xffu; @@ -63,7 +63,7 @@ STIN uint32_t htonl(uint32_t hostlong) { STIN uint16_t htons(uint16_t hostshort) { uint16_t res; unsigned char *p = (unsigned char *)&res; - *p++ = hostshort >> 8; + *p++ = (unsigned char)(hostshort >> 8); *p = hostshort & 0xffu; return res; } @@ -71,9 +71,9 @@ STIN uint16_t htons(uint16_t hostshort) { STIN uint32_t ntohl(uint32_t netlong) { uint32_t res; unsigned char *p = (unsigned char *)&netlong; - res = *p++ << 24; - res += *p++ << 16; - res += *p++ << 8; + res = (uint32_t)(*p++ << 24); + res += (uint32_t)(*p++ << 16); + res += (uint32_t)(*p++ << 8); res += *p; return res; } @@ -81,7 +81,7 @@ STIN uint32_t ntohl(uint32_t netlong) { STIN uint16_t ntohs(uint16_t netshort) { uint16_t res; unsigned char *p = (unsigned char *)&netshort; - res = *p++ << 8; + res = (uint16_t)(*p++ << 8); res += *p; return res; } diff --git a/deps/nghttp2/lib/nghttp2_option.c b/deps/nghttp2/lib/nghttp2_option.c index 34348e6606ccf4..ee0cd0f0226db9 100644 --- a/deps/nghttp2/lib/nghttp2_option.c +++ b/deps/nghttp2/lib/nghttp2_option.c @@ -90,6 +90,10 @@ void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ORIGIN; return; + case NGHTTP2_PRIORITY_UPDATE: + option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; + option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_PRIORITY_UPDATE; + return; default: return; } @@ -126,3 +130,16 @@ void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val) { option->opt_set_mask |= NGHTTP2_OPT_MAX_SETTINGS; option->max_settings = val; } + +void nghttp2_option_set_server_fallback_rfc7540_priorities( + nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES; + option->server_fallback_rfc7540_priorities = val; +} + +void nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation( + nghttp2_option *option, int val) { + option->opt_set_mask |= + NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION; + option->no_rfc9113_leading_and_trailing_ws_validation = val; +} diff --git a/deps/nghttp2/lib/nghttp2_option.h b/deps/nghttp2/lib/nghttp2_option.h index 939729fdcd5b6e..b228a0754c989e 100644 --- a/deps/nghttp2/lib/nghttp2_option.h +++ b/deps/nghttp2/lib/nghttp2_option.h @@ -68,6 +68,8 @@ typedef enum { NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10, NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11, NGHTTP2_OPT_MAX_SETTINGS = 1 << 12, + NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 13, + NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 14, } nghttp2_option_flag; /** @@ -127,6 +129,14 @@ struct nghttp2_option { * NGHTTP2_OPT_NO_CLOSED_STREAMS */ int no_closed_streams; + /** + * NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES + */ + int server_fallback_rfc7540_priorities; + /** + * NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION + */ + int no_rfc9113_leading_and_trailing_ws_validation; /** * NGHTTP2_OPT_USER_RECV_EXT_TYPES */ diff --git a/deps/nghttp2/lib/nghttp2_outbound_item.c b/deps/nghttp2/lib/nghttp2_outbound_item.c index f651c8029ac024..2a3041db195355 100644 --- a/deps/nghttp2/lib/nghttp2_outbound_item.c +++ b/deps/nghttp2/lib/nghttp2_outbound_item.c @@ -89,6 +89,9 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) { case NGHTTP2_ORIGIN: nghttp2_frame_origin_free(&frame->ext, mem); break; + case NGHTTP2_PRIORITY_UPDATE: + nghttp2_frame_priority_update_free(&frame->ext, mem); + break; default: assert(0); break; diff --git a/deps/nghttp2/lib/nghttp2_pq.c b/deps/nghttp2/lib/nghttp2_pq.c index bebccc76064198..64353acc961dc3 100644 --- a/deps/nghttp2/lib/nghttp2_pq.c +++ b/deps/nghttp2/lib/nghttp2_pq.c @@ -29,13 +29,12 @@ #include "nghttp2_helper.h" -int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) { +void nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) { pq->mem = mem; pq->capacity = 0; pq->q = NULL; pq->length = 0; pq->less = less; - return 0; } void nghttp2_pq_free(nghttp2_pq *pq) { diff --git a/deps/nghttp2/lib/nghttp2_pq.h b/deps/nghttp2/lib/nghttp2_pq.h index 7b7b7392f8479c..c8d90ef2cc8f33 100644 --- a/deps/nghttp2/lib/nghttp2_pq.h +++ b/deps/nghttp2/lib/nghttp2_pq.h @@ -55,14 +55,8 @@ typedef struct { /* * Initializes priority queue |pq| with compare function |cmp|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. */ -int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem); +void nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem); /* * Deallocates any resources allocated for |pq|. The stored items are diff --git a/deps/nghttp2/lib/nghttp2_session.c b/deps/nghttp2/lib/nghttp2_session.c index 380a47c1b1e82b..93f3f07cf782b3 100644 --- a/deps/nghttp2/lib/nghttp2_session.c +++ b/deps/nghttp2/lib/nghttp2_session.c @@ -36,6 +36,7 @@ #include "nghttp2_option.h" #include "nghttp2_http.h" #include "nghttp2_pq.h" +#include "nghttp2_extpri.h" #include "nghttp2_debug.h" /* @@ -143,6 +144,11 @@ static int session_detect_idle_stream(nghttp2_session *session, return 0; } +static int session_no_rfc7540_pri_no_fallback(nghttp2_session *session) { + return session->pending_no_rfc7540_priorities == 1 && + !session->fallback_rfc7540_priorities; +} + static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) { return (ext_types[type / 8] & (1 << (type & 0x7))) > 0; } @@ -354,6 +360,14 @@ static void session_inbound_frame_reset(nghttp2_session *session) { } nghttp2_frame_origin_free(&iframe->frame.ext, mem); break; + case NGHTTP2_PRIORITY_UPDATE: + if ((session->builtin_recv_ext_types & + NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) { + break; + } + /* Do not call nghttp2_frame_priority_update_free, because all + fields point to sbuf. */ + break; } } @@ -385,6 +399,7 @@ static void init_settings(nghttp2_settings_storage *settings) { settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN; settings->max_header_list_size = UINT32_MAX; + settings->no_rfc7540_priorities = UINT32_MAX; } static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, @@ -398,6 +413,21 @@ static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, aob->state = NGHTTP2_OB_POP_ITEM; } +#define NGHTTP2_STREAM_MAX_CYCLE_GAP ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX) + +static int stream_less(const void *lhsx, const void *rhsx) { + const nghttp2_stream *lhs, *rhs; + + lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry); + rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry); + + if (lhs->cycle == rhs->cycle) { + return lhs->seq < rhs->seq; + } + + return rhs->cycle - lhs->cycle <= NGHTTP2_STREAM_MAX_CYCLE_GAP; +} + int nghttp2_enable_strict_preface = 1; static int session_new(nghttp2_session **session_ptr, @@ -408,6 +438,7 @@ static int session_new(nghttp2_session **session_ptr, size_t nbuffer; size_t max_deflate_dynamic_table_size = NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; + size_t i; if (mem == NULL) { mem = nghttp2_mem_default(); @@ -442,6 +473,7 @@ static int session_new(nghttp2_session **session_ptr, (*session_ptr)->pending_local_max_concurrent_stream = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; (*session_ptr)->pending_enable_push = 1; + (*session_ptr)->pending_no_rfc7540_priorities = UINT8_MAX; if (server) { (*session_ptr)->server = 1; @@ -527,6 +559,20 @@ static int session_new(nghttp2_session **session_ptr, option->max_settings) { (*session_ptr)->max_settings = option->max_settings; } + + if ((option->opt_set_mask & + NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES) && + option->server_fallback_rfc7540_priorities) { + (*session_ptr)->opt_flags |= + NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES; + } + + if ((option->opt_set_mask & + NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) && + option->no_rfc9113_leading_and_trailing_ws_validation) { + (*session_ptr)->opt_flags |= + NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION; + } } rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, @@ -584,6 +630,10 @@ static int session_new(nghttp2_session **session_ptr, } } + for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { + nghttp2_pq_init(&(*session_ptr)->sched[i].ob_data, stream_less, mem); + } + return 0; fail_aob_framebuf: @@ -735,6 +785,7 @@ static void inflight_settings_del(nghttp2_inflight_settings *settings, void nghttp2_session_del(nghttp2_session *session) { nghttp2_mem *mem; nghttp2_inflight_settings *settings; + size_t i; if (session == NULL) { return; @@ -748,6 +799,9 @@ void nghttp2_session_del(nghttp2_session *session) { settings = next; } + for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { + nghttp2_pq_free(&session->sched[i].ob_data); + } nghttp2_stream_free(&session->root); /* Have to free streams first, so that we can check @@ -775,6 +829,8 @@ int nghttp2_session_reprioritize_stream( nghttp2_priority_spec pri_spec_default; const nghttp2_priority_spec *pri_spec = pri_spec_in; + assert((!session->server && session->pending_no_rfc7540_priorities != 1) || + (session->server && !session_no_rfc7540_pri_no_fallback(session))); assert(pri_spec->stream_id != stream->stream_id); if (!nghttp2_stream_in_dep_tree(stream)) { @@ -842,6 +898,214 @@ int nghttp2_session_reprioritize_stream( return 0; } +static uint64_t pq_get_first_cycle(nghttp2_pq *pq) { + nghttp2_stream *stream; + + if (nghttp2_pq_empty(pq)) { + return 0; + } + + stream = nghttp2_struct_of(nghttp2_pq_top(pq), nghttp2_stream, pq_entry); + return stream->cycle; +} + +static int session_ob_data_push(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + uint32_t urgency; + int inc; + nghttp2_pq *pq; + + assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES); + assert(stream->queued == 0); + + urgency = nghttp2_extpri_uint8_urgency(stream->extpri); + inc = nghttp2_extpri_uint8_inc(stream->extpri); + + assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS); + + pq = &session->sched[urgency].ob_data; + + stream->cycle = pq_get_first_cycle(pq); + if (inc) { + stream->cycle += stream->last_writelen; + } + + rv = nghttp2_pq_push(pq, &stream->pq_entry); + if (rv != 0) { + return rv; + } + + stream->queued = 1; + + return 0; +} + +static int session_ob_data_remove(nghttp2_session *session, + nghttp2_stream *stream) { + uint32_t urgency; + + assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES); + assert(stream->queued == 1); + + urgency = nghttp2_extpri_uint8_urgency(stream->extpri); + + assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS); + + nghttp2_pq_remove(&session->sched[urgency].ob_data, &stream->pq_entry); + + stream->queued = 0; + + return 0; +} + +static int session_attach_stream_item(nghttp2_session *session, + nghttp2_stream *stream, + nghttp2_outbound_item *item) { + int rv; + + rv = nghttp2_stream_attach_item(stream, item); + if (rv != 0) { + return rv; + } + + if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) { + return 0; + } + + return session_ob_data_push(session, stream); +} + +static int session_detach_stream_item(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + + rv = nghttp2_stream_detach_item(stream); + if (rv != 0) { + return rv; + } + + if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) || + !stream->queued) { + return 0; + } + + return session_ob_data_remove(session, stream); +} + +static int session_defer_stream_item(nghttp2_session *session, + nghttp2_stream *stream, uint8_t flags) { + int rv; + + rv = nghttp2_stream_defer_item(stream, flags); + if (rv != 0) { + return rv; + } + + if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) || + !stream->queued) { + return 0; + } + + return session_ob_data_remove(session, stream); +} + +static int session_resume_deferred_stream_item(nghttp2_session *session, + nghttp2_stream *stream, + uint8_t flags) { + int rv; + + rv = nghttp2_stream_resume_deferred_item(stream, flags); + if (rv != 0) { + return rv; + } + + if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) || + (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL)) { + return 0; + } + + return session_ob_data_push(session, stream); +} + +static nghttp2_outbound_item * +session_sched_get_next_outbound_item(nghttp2_session *session) { + size_t i; + nghttp2_pq_entry *ent; + nghttp2_stream *stream; + + for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { + ent = nghttp2_pq_top(&session->sched[i].ob_data); + if (!ent) { + continue; + } + + stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry); + return stream->item; + } + + return NULL; +} + +static int session_sched_empty(nghttp2_session *session) { + size_t i; + + for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { + if (!nghttp2_pq_empty(&session->sched[i].ob_data)) { + return 0; + } + } + + return 1; +} + +static void session_sched_reschedule_stream(nghttp2_session *session, + nghttp2_stream *stream) { + nghttp2_pq *pq; + uint32_t urgency = nghttp2_extpri_uint8_urgency(stream->extpri); + int inc = nghttp2_extpri_uint8_inc(stream->extpri); + uint64_t penalty = (uint64_t)stream->last_writelen; + int rv; + + (void)rv; + + assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS); + + pq = &session->sched[urgency].ob_data; + + if (!inc || nghttp2_pq_size(pq) == 1) { + return; + } + + nghttp2_pq_remove(pq, &stream->pq_entry); + + stream->cycle += penalty; + + rv = nghttp2_pq_push(pq, &stream->pq_entry); + + assert(0 == rv); +} + +static int session_update_stream_priority(nghttp2_session *session, + nghttp2_stream *stream, + uint8_t u8extpri) { + if (stream->extpri == u8extpri) { + return 0; + } + + if (stream->queued) { + session_ob_data_remove(session, stream); + + stream->extpri = u8extpri; + + return session_ob_data_push(session, stream); + } + + stream->extpri = u8extpri; + + return 0; +} + int nghttp2_session_add_item(nghttp2_session *session, nghttp2_outbound_item *item) { /* TODO Return error if stream is not found for the frame requiring @@ -863,7 +1127,7 @@ int nghttp2_session_add_item(nghttp2_session *session, return NGHTTP2_ERR_DATA_EXIST; } - rv = nghttp2_stream_attach_item(stream, item); + rv = session_attach_stream_item(session, stream, item); if (rv != 0) { return rv; @@ -1039,13 +1303,27 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, mem = &session->mem; stream = nghttp2_session_get_stream_raw(session, stream_id); + if (session->opt_flags & + NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) { + flags |= NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION; + } + if (stream) { assert(stream->state == NGHTTP2_STREAM_IDLE); - assert(nghttp2_stream_in_dep_tree(stream)); - nghttp2_session_detach_idle_stream(session, stream); - rv = nghttp2_stream_dep_remove(stream); - if (rv != 0) { - return NULL; + assert((stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) || + nghttp2_stream_in_dep_tree(stream)); + + if (nghttp2_stream_in_dep_tree(stream)) { + assert(!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)); + nghttp2_session_detach_idle_stream(session, stream); + rv = nghttp2_stream_dep_remove(stream); + if (rv != 0) { + return NULL; + } + + if (session_no_rfc7540_pri_no_fallback(session)) { + stream->flags |= NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES; + } } } else { stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream)); @@ -1056,7 +1334,21 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, stream_alloc = 1; } - if (pri_spec->stream_id != 0) { + if (session_no_rfc7540_pri_no_fallback(session) || + session->remote_settings.no_rfc7540_priorities == 1) { + /* For client which has not received server + SETTINGS_NO_RFC7540_PRIORITIES = 1, send a priority signal + opportunistically. */ + if (session->server || + session->remote_settings.no_rfc7540_priorities == 1) { + nghttp2_priority_spec_default_init(&pri_spec_default); + pri_spec = &pri_spec_default; + } + + if (session->pending_no_rfc7540_priorities == 1) { + flags |= NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES; + } + } else if (pri_spec->stream_id != 0) { dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); if (!dep_stream && @@ -1102,6 +1394,10 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, (int32_t)session->local_settings.initial_window_size, stream_user_data, mem); + if (session_no_rfc7540_pri_no_fallback(session)) { + stream->seq = session->stream_seq++; + } + rv = nghttp2_map_insert(&session->streams, stream_id, stream); if (rv != 0) { nghttp2_stream_free(stream); @@ -1141,6 +1437,10 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, } } + if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { + return stream; + } + if (pri_spec->stream_id == 0) { dep_stream = &session->root; } @@ -1180,7 +1480,7 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, item = stream->item; - rv = nghttp2_stream_detach_item(stream); + rv = session_detach_stream_item(session, stream); if (rv != 0) { return rv; @@ -1230,6 +1530,10 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, /* Closes both directions just in case they are not closed yet */ stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED; + if (session->pending_no_rfc7540_priorities == 1) { + return nghttp2_session_destroy_stream(session, stream); + } + if ((session->opt_flags & NGHTTP2_OPTMASK_NO_CLOSED_STREAMS) == 0 && session->server && !is_my_stream_id && nghttp2_stream_in_dep_tree(stream)) { @@ -1784,6 +2088,28 @@ static int session_predicate_origin_send(nghttp2_session *session) { return 0; } +static int session_predicate_priority_update_send(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return 0; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } + + return 0; +} + /* Take into account settings max frame size and both connection-level flow control here */ static ssize_t @@ -2013,7 +2339,7 @@ static int session_prep_frame(nghttp2_session *session, if (stream) { int rv2; - rv2 = nghttp2_stream_detach_item(stream); + rv2 = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv2)) { return rv2; @@ -2032,7 +2358,7 @@ static int session_prep_frame(nghttp2_session *session, queue when session->remote_window_size > 0 */ assert(session->remote_window_size > 0); - rv = nghttp2_stream_defer_item(stream, + rv = session_defer_stream_item(session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); if (nghttp2_is_fatal(rv)) { @@ -2051,7 +2377,8 @@ static int session_prep_frame(nghttp2_session *session, return rv; } if (rv == NGHTTP2_ERR_DEFERRED) { - rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER); + rv = session_defer_stream_item(session, stream, + NGHTTP2_STREAM_FLAG_DEFERRED_USER); if (nghttp2_is_fatal(rv)) { return rv; @@ -2062,7 +2389,7 @@ static int session_prep_frame(nghttp2_session *session, return NGHTTP2_ERR_DEFERRED; } if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_stream_detach_item(stream); + rv = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv)) { return rv; @@ -2078,7 +2405,7 @@ static int session_prep_frame(nghttp2_session *session, if (rv != 0) { int rv2; - rv2 = nghttp2_stream_detach_item(stream); + rv2 = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv2)) { return rv2; @@ -2328,6 +2655,18 @@ static int session_prep_frame(nghttp2_session *session, } return 0; + case NGHTTP2_PRIORITY_UPDATE: { + nghttp2_ext_priority_update *priority_update = frame->ext.payload; + rv = session_predicate_priority_update_send(session, + priority_update->stream_id); + if (rv != 0) { + return rv; + } + + nghttp2_frame_pack_priority_update(&session->aob.framebufs, &frame->ext); + + return 0; + } default: /* Unreachable here */ assert(0); @@ -2339,6 +2678,8 @@ static int session_prep_frame(nghttp2_session *session, nghttp2_outbound_item * nghttp2_session_get_next_ob_item(nghttp2_session *session) { + nghttp2_outbound_item *item; + if (nghttp2_outbound_queue_top(&session->ob_urgent)) { return nghttp2_outbound_queue_top(&session->ob_urgent); } @@ -2354,7 +2695,12 @@ nghttp2_session_get_next_ob_item(nghttp2_session *session) { } if (session->remote_window_size > 0) { - return nghttp2_stream_next_outbound_item(&session->root); + item = nghttp2_stream_next_outbound_item(&session->root); + if (item) { + return item; + } + + return session_sched_get_next_outbound_item(session); } return NULL; @@ -2388,7 +2734,12 @@ nghttp2_session_pop_next_ob_item(nghttp2_session *session) { } if (session->remote_window_size > 0) { - return nghttp2_stream_next_outbound_item(&session->root); + item = nghttp2_stream_next_outbound_item(&session->root); + if (item) { + return item; + } + + return session_sched_get_next_outbound_item(session); } return NULL; @@ -2498,10 +2849,20 @@ static int session_close_stream_on_goaway(nghttp2_session *session, return 0; } -static void reschedule_stream(nghttp2_stream *stream) { +static void session_reschedule_stream(nghttp2_session *session, + nghttp2_stream *stream) { stream->last_writelen = stream->item->frame.hd.length; - nghttp2_stream_reschedule(stream); + if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) { + nghttp2_stream_reschedule(stream); + return; + } + + if (!session->server) { + return; + } + + session_sched_reschedule_stream(session, stream); } static int session_update_stream_consumed_size(nghttp2_session *session, @@ -2550,7 +2911,7 @@ static int session_after_frame_sent1(nghttp2_session *session) { } if (stream && aux_data->eof) { - rv = nghttp2_stream_detach_item(stream); + rv = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv)) { return rv; } @@ -2675,9 +3036,8 @@ static int session_after_frame_sent1(nghttp2_session *session) { } } case NGHTTP2_PRIORITY: - if (session->server) { + if (session->server || session->pending_no_rfc7540_priorities == 1) { return 0; - ; } stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); @@ -2852,7 +3212,7 @@ static int session_after_frame_sent2(nghttp2_session *session) { further data. */ if (nghttp2_session_predicate_data_send(session, stream) != 0) { if (stream) { - rv = nghttp2_stream_detach_item(stream); + rv = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv)) { return rv; @@ -3150,7 +3510,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, } if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_stream_detach_item(stream); + rv = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv)) { return rv; @@ -3730,6 +4090,21 @@ static int session_end_stream_headers_received(nghttp2_session *session, nghttp2_frame *frame, nghttp2_stream *stream) { int rv; + + assert(frame->hd.type == NGHTTP2_HEADERS); + + if (session->server && session_enforce_http_messaging(session) && + frame->headers.cat == NGHTTP2_HCAT_REQUEST && + (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) && + !(stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) && + (stream->http_flags & NGHTTP2_HTTP_FLAG_PRIORITY)) { + rv = session_update_stream_priority(session, stream, stream->http_extpri); + if (rv != 0) { + assert(nghttp2_is_fatal(rv)); + return rv; + } + } + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { return 0; } @@ -4091,6 +4466,8 @@ int nghttp2_session_on_priority_received(nghttp2_session *session, int rv; nghttp2_stream *stream; + assert(!session_no_rfc7540_pri_no_fallback(session)); + if (frame->hd.stream_id == 0) { return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, "PRIORITY: stream_id == 0"); @@ -4148,6 +4525,8 @@ static int session_process_priority_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; + assert(!session_no_rfc7540_pri_no_fallback(session)); + nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos); return nghttp2_session_on_priority_received(session, frame); @@ -4214,8 +4593,8 @@ static int update_remote_initial_window_size_func(void *entry, void *ptr) { if (stream->remote_window_size > 0 && nghttp2_stream_check_deferred_by_flow_control(stream)) { - rv = nghttp2_stream_resume_deferred_item( - stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); + rv = session_resume_deferred_stream_item( + arg->session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); if (nghttp2_is_fatal(rv)) { return rv; @@ -4259,9 +4638,16 @@ static int update_local_initial_window_size_func(void *entry, void *ptr) { return nghttp2_session_add_rst_stream(arg->session, stream->stream_id, NGHTTP2_FLOW_CONTROL_ERROR); } - if (!(arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) && - stream->window_update_queued == 0 && - nghttp2_should_send_window_update(stream->local_window_size, + + if (stream->window_update_queued) { + return 0; + } + + if (arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { + return session_update_stream_consumed_size(arg->session, stream, 0); + } + + if (nghttp2_should_send_window_update(stream->local_window_size, stream->recv_window_size)) { rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE, @@ -4382,6 +4768,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session, case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: session->local_settings.enable_connect_protocol = iv[i].value; break; + case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: + session->local_settings.no_rfc7540_priorities = iv[i].value; + break; } } @@ -4541,6 +4930,34 @@ int nghttp2_session_on_settings_received(nghttp2_session *session, session->remote_settings.enable_connect_protocol = entry->value; break; + case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: + + if (entry->value != 0 && entry->value != 1) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: invalid SETTINGS_NO_RFC7540_PRIORITIES"); + } + + if (session->remote_settings.no_rfc7540_priorities != UINT32_MAX && + session->remote_settings.no_rfc7540_priorities != entry->value) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: SETTINGS_NO_RFC7540_PRIORITIES cannot be changed"); + } + + session->remote_settings.no_rfc7540_priorities = entry->value; + + break; + } + } + + if (session->remote_settings.no_rfc7540_priorities == UINT32_MAX) { + session->remote_settings.no_rfc7540_priorities = 0; + + if (session->server && session->pending_no_rfc7540_priorities && + (session->opt_flags & + NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES)) { + session->fallback_rfc7540_priorities = 1; } } @@ -4826,8 +5243,8 @@ static int session_on_stream_window_update_received(nghttp2_session *session, if (stream->remote_window_size > 0 && nghttp2_stream_check_deferred_by_flow_control(stream)) { - rv = nghttp2_stream_resume_deferred_item( - stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); + rv = session_resume_deferred_stream_item( + session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); if (nghttp2_is_fatal(rv)) { return rv; @@ -4898,6 +5315,80 @@ int nghttp2_session_on_origin_received(nghttp2_session *session, return session_call_on_frame_received(session, frame); } +int nghttp2_session_on_priority_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + nghttp2_ext_priority_update *priority_update; + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec; + nghttp2_extpri extpri; + int rv; + + assert(session->server); + + priority_update = frame->ext.payload; + + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "PRIORITY_UPDATE: stream_id == 0"); + } + + if (nghttp2_session_is_my_stream_id(session, priority_update->stream_id)) { + if (session_detect_idle_stream(session, priority_update->stream_id)) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "PRIORITY_UPDATE: prioritizing idle push is not allowed"); + } + + /* TODO Ignore priority signal to a push stream for now */ + return session_call_on_frame_received(session, frame); + } + + stream = nghttp2_session_get_stream_raw(session, priority_update->stream_id); + if (stream) { + /* Stream already exists. */ + if (stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) { + return session_call_on_frame_received(session, frame); + } + } else if (session_detect_idle_stream(session, priority_update->stream_id)) { + if (session->num_idle_streams + session->num_incoming_streams >= + session->local_settings.max_concurrent_streams) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "PRIORITY_UPDATE: max concurrent streams exceeded"); + } + + nghttp2_priority_spec_default_init(&pri_spec); + stream = nghttp2_session_open_stream(session, priority_update->stream_id, + NGHTTP2_FLAG_NONE, &pri_spec, + NGHTTP2_STREAM_IDLE, NULL); + if (!stream) { + return NGHTTP2_ERR_NOMEM; + } + } else { + return session_call_on_frame_received(session, frame); + } + + extpri.urgency = NGHTTP2_EXTPRI_DEFAULT_URGENCY; + extpri.inc = 0; + + rv = nghttp2_http_parse_priority(&extpri, priority_update->field_value, + priority_update->field_value_len); + if (rv != 0) { + /* Just ignore field_value if it cannot be parsed. */ + return session_call_on_frame_received(session, frame); + } + + rv = session_update_stream_priority(session, stream, + nghttp2_extpri_to_uint8(&extpri)); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + return session_call_on_frame_received(session, frame); +} + static int session_process_altsvc_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; @@ -4932,6 +5423,16 @@ static int session_process_origin_frame(nghttp2_session *session) { return nghttp2_session_on_origin_received(session, frame); } +static int session_process_priority_update_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_priority_update_payload(&frame->ext, iframe->sbuf.pos, + nghttp2_buf_len(&iframe->sbuf)); + + return nghttp2_session_on_priority_update_received(session, frame); +} + static int session_process_extension_frame(nghttp2_session *session) { int rv; nghttp2_inbound_frame *iframe = &session->iframe; @@ -5269,6 +5770,7 @@ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) { case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: + case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: break; default: DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id); @@ -5882,6 +6384,49 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD; + break; + case NGHTTP2_PRIORITY_UPDATE: + if ((session->builtin_recv_ext_types & + NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + + DEBUGF("recv: PRIORITY_UPDATE\n"); + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + iframe->frame.ext.payload = + &iframe->ext_frame_payload.priority_update; + + if (!session->server) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "PRIORITY_UPDATE is received from server"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + + if (iframe->payloadleft < 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + if (!session_no_rfc7540_pri_no_fallback(session) || + iframe->payloadleft > sizeof(iframe->raw_sbuf)) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + + busy = 1; + + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, iframe->payloadleft); + break; default: busy = 1; @@ -5988,13 +6533,16 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, break; case NGHTTP2_PRIORITY: - rv = session_process_priority_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } + if (!session_no_rfc7540_pri_no_fallback(session) && + session->remote_settings.no_rfc7540_priorities != 1) { + rv = session_process_priority_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } } session_inbound_frame_reset(session); @@ -6150,6 +6698,18 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, iframe->state = NGHTTP2_IB_READ_ALTSVC_PAYLOAD; + break; + case NGHTTP2_PRIORITY_UPDATE: + DEBUGF("recv: prioritized_stream_id=%d\n", + nghttp2_get_uint32(iframe->sbuf.pos) & NGHTTP2_STREAM_ID_MASK); + + rv = session_process_priority_update_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + break; } default: @@ -6863,7 +7423,8 @@ int nghttp2_session_want_write(nghttp2_session *session) { */ return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) || nghttp2_outbound_queue_top(&session->ob_reg) || - (!nghttp2_pq_empty(&session->root.obq) && + ((!nghttp2_pq_empty(&session->root.obq) || + !session_sched_empty(session)) && session->remote_window_size > 0) || (nghttp2_outbound_queue_top(&session->ob_syn) && !session_is_outgoing_concurrent_streams_max(session)); @@ -7016,6 +7577,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, int rv; nghttp2_mem *mem; nghttp2_inflight_settings *inflight_settings = NULL; + uint8_t no_rfc7540_pri = session->pending_no_rfc7540_priorities; mem = &session->mem; @@ -7033,6 +7595,21 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, return NGHTTP2_ERR_INVALID_ARGUMENT; } + for (i = 0; i < niv; ++i) { + if (iv[i].settings_id != NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES) { + continue; + } + + if (no_rfc7540_pri == UINT8_MAX) { + no_rfc7540_pri = (uint8_t)iv[i].value; + continue; + } + + if (iv[i].value != (uint32_t)no_rfc7540_pri) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { return NGHTTP2_ERR_NOMEM; @@ -7107,6 +7684,12 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, } } + if (no_rfc7540_pri == UINT8_MAX) { + session->pending_no_rfc7540_priorities = 0; + } else { + session->pending_no_rfc7540_priorities = no_rfc7540_pri; + } + return 0; } @@ -7235,7 +7818,7 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, return rv; } - reschedule_stream(stream); + session_reschedule_stream(session, stream); if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) && (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) { @@ -7309,7 +7892,7 @@ int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) { return NGHTTP2_ERR_INVALID_ARGUMENT; } - rv = nghttp2_stream_resume_deferred_item(stream, + rv = session_resume_deferred_stream_item(session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER); if (nghttp2_is_fatal(rv)) { @@ -7417,6 +8000,8 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, return session->remote_settings.max_header_list_size; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: return session->remote_settings.enable_connect_protocol; + case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: + return session->remote_settings.no_rfc7540_priorities; } assert(0); @@ -7440,6 +8025,8 @@ uint32_t nghttp2_session_get_local_settings(nghttp2_session *session, return session->local_settings.max_header_list_size; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: return session->local_settings.enable_connect_protocol; + case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: + return session->local_settings.no_rfc7540_priorities; } assert(0); @@ -7723,6 +8310,10 @@ int nghttp2_session_change_stream_priority( nghttp2_stream *stream; nghttp2_priority_spec pri_spec_copy; + if (session->pending_no_rfc7540_priorities == 1) { + return 0; + } + if (stream_id == 0 || stream_id == pri_spec->stream_id) { return NGHTTP2_ERR_INVALID_ARGUMENT; } @@ -7755,6 +8346,10 @@ int nghttp2_session_create_idle_stream(nghttp2_session *session, nghttp2_stream *stream; nghttp2_priority_spec pri_spec_copy; + if (session->pending_no_rfc7540_priorities == 1) { + return 0; + } + if (stream_id == 0 || stream_id == pri_spec->stream_id || !session_detect_idle_stream(session, stream_id)) { return NGHTTP2_ERR_INVALID_ARGUMENT; @@ -7796,3 +8391,38 @@ nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) { void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) { session->user_data = user_data; } + +int nghttp2_session_change_extpri_stream_priority( + nghttp2_session *session, int32_t stream_id, + const nghttp2_extpri *extpri_in, int ignore_client_signal) { + nghttp2_stream *stream; + nghttp2_extpri extpri = *extpri_in; + + if (!session->server) { + return NGHTTP2_ERR_INVALID_STATE; + } + + if (session->pending_no_rfc7540_priorities != 1) { + return 0; + } + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + stream = nghttp2_session_get_stream_raw(session, stream_id); + if (!stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (extpri.urgency > NGHTTP2_EXTPRI_URGENCY_LOW) { + extpri.urgency = NGHTTP2_EXTPRI_URGENCY_LOW; + } + + if (ignore_client_signal) { + stream->flags |= NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES; + } + + return session_update_stream_priority(session, stream, + nghttp2_extpri_to_uint8(&extpri)); +} diff --git a/deps/nghttp2/lib/nghttp2_session.h b/deps/nghttp2/lib/nghttp2_session.h index 907b1704bc8412..34d2d58528a796 100644 --- a/deps/nghttp2/lib/nghttp2_session.h +++ b/deps/nghttp2/lib/nghttp2_session.h @@ -52,7 +52,9 @@ typedef enum { NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1, NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2, NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3, - NGHTTP2_OPTMASK_NO_CLOSED_STREAMS = 1 << 4 + NGHTTP2_OPTMASK_NO_CLOSED_STREAMS = 1 << 4, + NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 5, + NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 6, } nghttp2_optmask; /* @@ -62,7 +64,8 @@ typedef enum { typedef enum { NGHTTP2_TYPEMASK_NONE = 0, NGHTTP2_TYPEMASK_ALTSVC = 1 << 0, - NGHTTP2_TYPEMASK_ORIGIN = 1 << 1 + NGHTTP2_TYPEMASK_ORIGIN = 1 << 1, + NGHTTP2_TYPEMASK_PRIORITY_UPDATE = 1 << 2 } nghttp2_typemask; typedef enum { @@ -151,10 +154,8 @@ typedef struct { /* padding length for the current frame */ size_t padlen; nghttp2_inbound_state state; - /* Small buffer. Currently the largest contiguous chunk to buffer - is frame header. We buffer part of payload, but they are smaller - than frame header. */ - uint8_t raw_sbuf[NGHTTP2_FRAME_HDLEN]; + /* Small fixed sized buffer. */ + uint8_t raw_sbuf[32]; } nghttp2_inbound_frame; typedef struct { @@ -165,6 +166,7 @@ typedef struct { uint32_t max_frame_size; uint32_t max_header_list_size; uint32_t enable_connect_protocol; + uint32_t no_rfc7540_priorities; } nghttp2_settings_storage; typedef enum { @@ -202,6 +204,12 @@ struct nghttp2_session { response) frame, which are subject to SETTINGS_MAX_CONCURRENT_STREAMS limit. */ nghttp2_outbound_queue ob_syn; + /* Queues for DATA frames which is used when + SETTINGS_NO_RFC7540_PRIORITIES is enabled. This implements RFC + 9218 extensible prioritization scheme. */ + struct { + nghttp2_pq ob_data; + } sched[NGHTTP2_EXTPRI_URGENCY_LEVELS]; nghttp2_active_outbound_item aob; nghttp2_inbound_frame iframe; nghttp2_hd_deflater hd_deflater; @@ -227,6 +235,9 @@ struct nghttp2_session { /* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not considered as in-flight. */ nghttp2_inflight_settings *inflight_settings_head; + /* Sequential number across all streams to process streams in + FIFO. */ + uint64_t stream_seq; /* The number of outgoing streams. This will be capped by remote_settings.max_concurrent_streams. */ size_t num_outgoing_streams; @@ -328,6 +339,11 @@ struct nghttp2_session { /* Unacked local ENABLE_CONNECT_PROTOCOL value. We use this to accept :protocol header field before SETTINGS_ACK is received. */ uint8_t pending_enable_connect_protocol; + /* Unacked local SETTINGS_NO_RFC7540_PRIORITIES value, which is + effective before it is acknowledged. */ + uint8_t pending_no_rfc7540_priorities; + /* Turn on fallback to RFC 7540 priorities; for server use only. */ + uint8_t fallback_rfc7540_priorities; /* Nonzero if the session is server side. */ uint8_t server; /* Flags indicating GOAWAY is sent and/or received. The flags are @@ -773,6 +789,19 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session, int nghttp2_session_on_origin_received(nghttp2_session *session, nghttp2_frame *frame); +/* + * Called when PRIORITY_UPDATE is received, assuming |frame| is + * properly initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_priority_update_received(nghttp2_session *session, + nghttp2_frame *frame); + /* * Called when DATA is received, assuming |frame| is properly * initialized. diff --git a/deps/nghttp2/lib/nghttp2_stream.c b/deps/nghttp2/lib/nghttp2_stream.c index f4c80a24b5e704..b3614a0b02761e 100644 --- a/deps/nghttp2/lib/nghttp2_stream.c +++ b/deps/nghttp2/lib/nghttp2_stream.c @@ -100,6 +100,8 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, stream->descendant_next_seq = 0; stream->seq = 0; stream->last_writelen = 0; + + stream->extpri = stream->http_extpri = NGHTTP2_EXTPRI_DEFAULT_URGENCY; } void nghttp2_stream_free(nghttp2_stream *stream) { @@ -484,6 +486,10 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream, stream->item = item; + if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { + return 0; + } + rv = stream_update_dep_on_attach_item(stream); if (rv != 0) { /* This may relave stream->queued == 1, but stream->item == NULL. @@ -503,6 +509,10 @@ int nghttp2_stream_detach_item(nghttp2_stream *stream) { stream->item = NULL; stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL); + if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { + return 0; + } + return stream_update_dep_on_detach_item(stream); } @@ -514,6 +524,10 @@ int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) { stream->flags |= flags; + if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { + return 0; + } + return stream_update_dep_on_detach_item(stream); } @@ -529,6 +543,10 @@ int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) { return 0; } + if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { + return 0; + } + return stream_update_dep_on_attach_item(stream); } diff --git a/deps/nghttp2/lib/nghttp2_stream.h b/deps/nghttp2/lib/nghttp2_stream.h index 2846c6aab75705..7a8e4c6c1ddb08 100644 --- a/deps/nghttp2/lib/nghttp2_stream.h +++ b/deps/nghttp2/lib/nghttp2_stream.h @@ -90,8 +90,15 @@ typedef enum { NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08, /* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and NGHTTP2_STREAM_FLAG_DEFERRED_USER. */ - NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c - + NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c, + /* Indicates that this stream is not subject to RFC7540 + priorities scheme. */ + NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES = 0x10, + /* Ignore client RFC 9218 priority signal. */ + NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES = 0x20, + /* Indicates that RFC 9113 leading and trailing white spaces + validation against a field value is not performed. */ + NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 0x40, } nghttp2_stream_flag; /* HTTP related flags to enforce HTTP semantics */ @@ -132,6 +139,11 @@ typedef enum { /* set if final response is expected */ NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14, NGHTTP2_HTTP_FLAG__PROTOCOL = 1 << 15, + /* set if priority header field is received */ + NGHTTP2_HTTP_FLAG_PRIORITY = 1 << 16, + /* set if an error is encountered while parsing priority header + field */ + NGHTTP2_HTTP_FLAG_BAD_PRIORITY = 1 << 17, } nghttp2_http_flag; struct nghttp2_stream { @@ -204,7 +216,7 @@ struct nghttp2_stream { /* status code from remote server */ int16_t status_code; /* Bitwise OR of zero or more nghttp2_http_flag values */ - uint16_t http_flags; + uint32_t http_flags; /* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */ uint8_t flags; /* Bitwise OR of zero or more nghttp2_shut_flag values */ @@ -218,6 +230,12 @@ struct nghttp2_stream { this stream. The nonzero does not necessarily mean WINDOW_UPDATE is not queued. */ uint8_t window_update_queued; + /* extpri is a stream priority produced by nghttp2_extpri_to_uint8 + used by RFC 9218 extensible priorities. */ + uint8_t extpri; + /* http_extpri is a stream priority received in HTTP request header + fields and produced by nghttp2_extpri_to_uint8. */ + uint8_t http_extpri; }; void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, diff --git a/deps/nghttp2/lib/nghttp2_submit.c b/deps/nghttp2/lib/nghttp2_submit.c index 92fb03e8ca0f09..f5554eb56494ed 100644 --- a/deps/nghttp2/lib/nghttp2_submit.c +++ b/deps/nghttp2/lib/nghttp2_submit.c @@ -196,7 +196,8 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, flags &= NGHTTP2_FLAG_END_STREAM; - if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { + if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) && + session->remote_settings.no_rfc7540_priorities != 1) { rv = detect_self_dependency(session, stream_id, pri_spec); if (rv != 0) { return rv; @@ -229,6 +230,10 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, mem = &session->mem; + if (session->remote_settings.no_rfc7540_priorities == 1) { + return 0; + } + if (stream_id == 0 || pri_spec == NULL) { return NGHTTP2_ERR_INVALID_ARGUMENT; } @@ -662,6 +667,78 @@ int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags, return rv; } +int nghttp2_submit_priority_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const uint8_t *field_value, + size_t field_value_len) { + nghttp2_mem *mem; + uint8_t *buf, *p; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_ext_priority_update *priority_update; + int rv; + (void)flags; + + mem = &session->mem; + + if (session->server) { + return NGHTTP2_ERR_INVALID_STATE; + } + + if (session->remote_settings.no_rfc7540_priorities == 0) { + return 0; + } + + if (stream_id == 0 || 4 + field_value_len > NGHTTP2_MAX_PAYLOADLEN) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (field_value_len) { + buf = nghttp2_mem_malloc(mem, field_value_len + 1); + if (buf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + p = nghttp2_cpymem(buf, field_value, field_value_len); + *p = '\0'; + } else { + buf = NULL; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail_item_malloc; + } + + nghttp2_outbound_item_init(item); + + item->aux_data.ext.builtin = 1; + + priority_update = &item->ext_frame_payload.priority_update; + + frame = &item->frame; + frame->ext.payload = priority_update; + + nghttp2_frame_priority_update_init(&frame->ext, stream_id, buf, + field_value_len); + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_priority_update_free(&frame->ext, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + return 0; + +fail_item_malloc: + free(buf); + + return rv; +} + static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec, const nghttp2_data_provider *data_prd) { uint8_t flags = NGHTTP2_FLAG_NONE; @@ -688,7 +765,8 @@ int32_t nghttp2_submit_request(nghttp2_session *session, return NGHTTP2_ERR_PROTO; } - if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { + if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) && + session->remote_settings.no_rfc7540_priorities != 1) { rv = detect_self_dependency(session, -1, pri_spec); if (rv != 0) { return rv; diff --git a/deps/nghttp2/nghttp2.gyp b/deps/nghttp2/nghttp2.gyp index 0dcd034b8169da..c4a18650efe789 100644 --- a/deps/nghttp2/nghttp2.gyp +++ b/deps/nghttp2/nghttp2.gyp @@ -38,6 +38,7 @@ 'lib/nghttp2_buf.c', 'lib/nghttp2_callbacks.c', 'lib/nghttp2_debug.c', + 'lib/nghttp2_extpri.c', 'lib/nghttp2_frame.c', 'lib/nghttp2_hd.c', 'lib/nghttp2_hd_huffman.c', From 3a02d50d35d91fdd5073d9f7c20e4476a60f7a19 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Tue, 22 Nov 2022 11:06:43 -0500 Subject: [PATCH 04/97] doc: add link to doc with social processes Signed-off-by: Michael Dawson PR-URL: https://github.com/nodejs/node/pull/45584 Reviewed-By: Luigi Pinca Reviewed-By: Rich Trott --- doc/contributing/suggesting-social-media-posts.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 doc/contributing/suggesting-social-media-posts.md diff --git a/doc/contributing/suggesting-social-media-posts.md b/doc/contributing/suggesting-social-media-posts.md new file mode 100644 index 00000000000000..7b7200ee7bac88 --- /dev/null +++ b/doc/contributing/suggesting-social-media-posts.md @@ -0,0 +1,5 @@ +# Suggesting Social Media Posts + +Node.js social media is managed by OpenJS Foundation staff. The processes are +documented in +[Node.js Social Amplification Request Guidelines](https://docs.google.com/document/d/1yrYZJ2twrbpUuScbo3rmN_v-Jfv6d2tO74nCT6PcpxI). From 7892e23e68bea057a38afb542a6dd5d325e3e42f Mon Sep 17 00:00:00 2001 From: Kohei Ueno Date: Sat, 26 Nov 2022 19:51:15 +0900 Subject: [PATCH 05/97] repl: do not define `wasi` on global with no flag PR-URL: https://github.com/nodejs/node/pull/45595 Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig Reviewed-By: Antoine du Hamel Reviewed-By: Luigi Pinca --- lib/internal/main/repl.js | 2 +- test/parallel/test-repl-built-in-modules.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/internal/main/repl.js b/lib/internal/main/repl.js index 1fb48774ca6849..c371706b352c82 100644 --- a/lib/internal/main/repl.js +++ b/lib/internal/main/repl.js @@ -8,7 +8,6 @@ const { markBootstrapComplete } = require('internal/process/pre_execution'); -const esmLoader = require('internal/process/esm_loader'); const { evalScript } = require('internal/process/execution'); @@ -37,6 +36,7 @@ if (process.env.NODE_REPL_EXTERNAL_MODULE) { process.exit(kGenericUserError); } + const esmLoader = require('internal/process/esm_loader'); esmLoader.loadESM(() => { console.log(`Welcome to Node.js ${process.version}.\n` + 'Type ".help" for more information.'); diff --git a/test/parallel/test-repl-built-in-modules.js b/test/parallel/test-repl-built-in-modules.js index b1845d83e68332..6273367e65a2a7 100644 --- a/test/parallel/test-repl-built-in-modules.js +++ b/test/parallel/test-repl-built-in-modules.js @@ -30,3 +30,19 @@ assert.doesNotMatch( stdout, /Uncaught Error: Cannot find module 'wasi'[\w\W]+- \n/); assert.match(stdout, /{ WASI: \[class WASI\] }/); + +{ + const res = cp.execFileSync(process.execPath, ['-i'], { + input: "'wasi' in global", + encoding: 'utf8', + }); + // `wasi` shouldn't be defined on global when the flag is not set + assert.match(res, /false\n/); +} +{ + const res = cp.execFileSync(process.execPath, ['-i', '--experimental-wasi-unstable-preview1'], { + input: "'wasi' in global", + encoding: 'utf8', + }); + assert.match(res, /true\n/); +} From 358e2fe21779f4ac3905ae86640abb092567dc31 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 21 Nov 2022 22:44:11 +0100 Subject: [PATCH 06/97] test,crypto: update WebCryptoAPI WPT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/45569 Reviewed-By: Antoine du Hamel Reviewed-By: Tobias Nießen --- test/fixtures/wpt/README.md | 2 +- .../import_export/okp_importKey_failures.js | 176 ++++++++++++++++++ ...kp_importKey_failures_Ed25519.https.any.js | 102 ++++++++++ .../okp_importKey_failures_Ed448.https.any.js | 103 ++++++++++ ...okp_importKey_failures_X25519.https.any.js | 102 ++++++++++ .../okp_importKey_failures_X448.https.any.js | 103 ++++++++++ .../wpt/WebCryptoAPI/sign_verify/ecdsa.js | 27 +++ .../WebCryptoAPI/sign_verify/ecdsa_vectors.js | 76 +++++--- .../wpt/WebCryptoAPI/sign_verify/eddsa.js | 12 ++ .../wpt/WebCryptoAPI/sign_verify/rsa.js | 12 +- .../wrapKey_unwrapKey.https.any.js | 12 +- test/fixtures/wpt/versions.json | 2 +- test/wpt/status/WebCryptoAPI.json | 148 +++++++++++++++ 13 files changed, 847 insertions(+), 30 deletions(-) create mode 100644 test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures.js create mode 100644 test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js create mode 100644 test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.https.any.js create mode 100644 test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js create mode 100644 test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.https.any.js diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index 6e2ce815a454fd..147d695df1ed64 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -32,7 +32,7 @@ Last update: - user-timing: https://github.com/web-platform-tests/wpt/tree/df24fb604e/user-timing - wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/d8dbe6990b/wasm/jsapi - wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi -- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/0042d42ee6/WebCryptoAPI +- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/21ccdcd814/WebCryptoAPI - webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/a370aad338/webidl/ecmascript-binding/es-exceptions [Web Platform Tests]: https://github.com/web-platform-tests/wpt diff --git a/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures.js b/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures.js new file mode 100644 index 00000000000000..4e2d717595127b --- /dev/null +++ b/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures.js @@ -0,0 +1,176 @@ +function run_test(algorithmNames) { + var subtle = crypto.subtle; // Change to test prefixed implementations + + setup({explicit_timeout: true}); + +// These tests check that importKey and exportKey throw an error, and that +// the error is of the right type, for a wide set of incorrect parameters. + +// Error testing occurs by setting the parameter that should trigger the +// error to an invalid value, then combining that with all valid +// parameters that should be checked earlier by importKey, and all +// valid and invalid parameters that should be checked later by +// importKey. +// +// There are a lot of combinations of possible parameters for both +// success and failure modes, resulting in a very large number of tests +// performed. + + + var allTestVectors = [ // Parameters that should work for importKey / exportKey + {name: "Ed25519", privateUsages: ["sign"], publicUsages: ["verify"]}, + {name: "Ed448", privateUsages: ["sign"], publicUsages: ["verify"]}, + {name: "X25519", privateUsages: ["deriveKey", "deriveBits"], publicUsages: []}, + {name: "X448", privateUsages: ["deriveKey", "deriveBits"], publicUsages: []}, + ]; + + var testVectors = []; + if (algorithmNames && !Array.isArray(algorithmNames)) { + algorithmNames = [algorithmNames]; + }; + allTestVectors.forEach(function(vector) { + if (!algorithmNames || algorithmNames.includes(vector.name)) { + testVectors.push(vector); + } + }); + + function parameterString(format, algorithm, extractable, usages, data) { + if (typeof algorithm !== "object" && typeof algorithm !== "string") { + alert(algorithm); + } + + var jwk_label = ""; + if (format === "jwk") + jwk_label = data.d === undefined ? " (public) " : "(private)"; + + var result = "(" + + objectToString(format) + jwk_label + ", " + + objectToString(algorithm) + ", " + + objectToString(extractable) + ", " + + objectToString(usages) + + ")"; + + return result; + } + + // Test that a given combination of parameters results in an error, + // AND that it is the correct kind of error. + // + // Expected error is either a number, tested against the error code, + // or a string, tested against the error name. + function testError(format, algorithm, keyData, keySize, usages, extractable, expectedError, testTag) { + promise_test(async() => { + let key; + try { + key = await subtle.importKey(format, keyData, algorithm, extractable, usages); + } catch(err) { + let actualError = typeof expectedError === "number" ? err.code : err.name; + assert_equals(actualError, expectedError, testTag + " not supported."); + } + assert_equals(key, undefined, "Operation succeeded, but should not have."); + }, testTag + ": importKey" + parameterString(format, algorithm, extractable, usages, keyData)); + } + + // Don't create an exhaustive list of all invalid usages, + // because there would usually be nearly 2**8 of them, + // way too many to test. Instead, create every singleton + // of an illegal usage, and "poison" every valid usage + // with an illegal one. + function invalidUsages(validUsages, mandatoryUsages) { + var results = []; + + var illegalUsages = []; + ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey", "deriveKey", "deriveBits"].forEach(function(usage) { + if (!validUsages.includes(usage)) { + illegalUsages.push(usage); + } + }); + + var goodUsageCombinations = validUsages.length === 0 ? [] : allValidUsages(validUsages, false, mandatoryUsages); + + illegalUsages.forEach(function(illegalUsage) { + results.push([illegalUsage]); + goodUsageCombinations.forEach(function(usageCombination) { + results.push(usageCombination.concat([illegalUsage])); + }); + }); + + return results; + } + + function validUsages(usages, format, data) { + if (format === 'spki') return usages.publicUsages + if (format === 'pkcs8') return usages.privateUsages + if (format === 'jwk') { + if (data === undefined) + return []; + return data.d === undefined ? usages.publicUsages : usages.privateUsages; + } + return []; + } + +// Now test for properly handling errors +// - Unsupported algorithm +// - Bad usages for algorithm +// - Bad key lengths +// - Lack of a mandatory format field +// - Incompatible keys pair + + // Algorithms normalize okay, but usages bad (though not empty). + // It shouldn't matter what other extractable is. Should fail + // due to SyntaxError + testVectors.forEach(function(vector) { + var name = vector.name; + validKeyData.forEach(function(test) { + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + invalidUsages(validUsages(vector, test.format, test.data)).forEach(function(usages) { + [true, false].forEach(function(extractable) { + testError(test.format, algorithm, test.data, name, usages, extractable, "SyntaxError", "Bad usages"); + }); + }); + }); + }); + }); + + // Algorithms normalize okay, usages ok. The length of the key must thouw a DataError exception. + testVectors.forEach(function(vector) { + var name = vector.name; + badKeyLengthData.forEach(function(test) { + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + allValidUsages(validUsages(vector, test.format, test.data)).forEach(function(usages) { + [true, false].forEach(function(extractable) { + testError(test.format, algorithm, test.data, name, usages, extractable, "DataError", "Bad key length"); + }); + }); + }); + }); + }); + + // Algorithms normalize okay, usages ok and valid key. The lack of the mandatory JWK parameter must throw a syntax error. + testVectors.forEach(function(vector) { + var name = vector.name; + missingJWKFieldKeyData.forEach(function(test) { + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + allValidUsages(validUsages(vector, 'jwk', test.data)).forEach(function(usages) { + [true, false].forEach(function(extractable) { + testError('jwk', algorithm, test.data, name, usages, extractable, "DataError", "Missing JWK '" + test.param + "' parameter"); + }); + }); + }); + }); + }); + + // Algorithms normalize okay, usages ok and valid key. The public key is not compatible with the private key. + testVectors.forEach(function(vector) { + var name = vector.name; + invalidJWKKeyData.forEach(function(data) { + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + allValidUsages(vector.privateUsages).forEach(function(usages) { + [true].forEach(function(extractable) { + testError('jwk', algorithm, data, name, usages, extractable, "DataError", "Invalid key pair"); + }); + }); + }); + }); + }); +} diff --git a/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js b/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js new file mode 100644 index 00000000000000..7d6ec6171c3e8f --- /dev/null +++ b/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js @@ -0,0 +1,102 @@ +// META: title=WebCryptoAPI: importKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=okp_importKey_failures.js + +// Setup: define the correct behaviors that should be sought, and create +// helper functions that generate all possible test parameters for +// different situations. +var validKeyData = [ + { + format: "spki", + data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204]) + }, + { + format: "pkcs8", + data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168, 31]) + }, + { + format: "jwk", + data: { + crv: "Ed25519", + d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8", + x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "Ed25519", + x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw", + kty: "OKP" + }, + }, +]; + +// Removed just the last byte. +var badKeyLengthData = [ + { + format: "spki", + data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61]) + }, + { + format: "pkcs8", + data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168]) + }, + { + format: "jwk", + data: { + crv: "Ed25519", + d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB", + x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw", + kty: "OKP" + } + }, + { + format: "jwk", + data: { + crv: "Ed25519", + x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPc", + kty: "OKP" + } + }, +]; + +var missingJWKFieldKeyData = [ + { + param: "x", + data: { + crv: "Ed25519", + d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8", + kty: "OKP" + }, + }, + { + param: "kty", + data: { + crv: "Ed25519", + x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8", + }, + }, + { + param: "crv", + data: { + x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + kty: "OKP" + }, + } +]; + +// The public key doesn't match the private key. +var invalidJWKKeyData = [ + { + crv: "Ed25519", + d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8", + x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + kty: "OKP" + }, +]; + +run_test(["Ed25519"]); diff --git a/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.https.any.js b/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.https.any.js new file mode 100644 index 00000000000000..1035800fafa394 --- /dev/null +++ b/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.https.any.js @@ -0,0 +1,103 @@ +// META: title=WebCryptoAPI: importKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=okp_importKey_failures.js + +// Setup: define the correct behaviors that should be sought, and create +// helper functions that generate all possible test parameters for +// different situations. +var validKeyData = [ + { + format: "spki", + data: new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29, 46]), + }, + { + format: "jwk", + data: { + crv: "Ed448", + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "Ed448", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA", + kty: "OKP" + }, + }, +]; + +// Removed just the last byte. +var badKeyLengthData = [ + { + format: "spki", + data: new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29]), + }, + { + format: "jwk", + data: { + crv: "Ed448", + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "Ed448", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalq", + kty: "OKP" + }, + }, +]; + +var missingJWKFieldKeyData = [ + { + param: "x", + data: { + crv: "Ed448", + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u", + kty: "OKP" + } + }, + { + param: "kty", + data: { + crv: "Ed448", + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA", + } + }, + { + param: "crv", + data: { + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA", + kty: "OKP" + } + } +]; + +// The public key doesn't match the private key. +var invalidJWKKeyData = [ + { + crv: "Ed448", + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u", + x: "X9dEm1m0Yf0s54fsYWrUah2hNCSFpw4fig6nXYDpZ3jt8SR2m0bHBhvWeD3x5Q9s0foavq_oJWGA", + kty: "OKP" + }, +]; + +run_test(["Ed448"]); diff --git a/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js b/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js new file mode 100644 index 00000000000000..fe5fd54da53d2d --- /dev/null +++ b/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js @@ -0,0 +1,102 @@ +// META: title=WebCryptoAPI: importKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=okp_importKey_failures.js + +// Setup: define the correct behaviors that should be sought, and create +// helper functions that generate all possible test parameters for +// different situations. +var validKeyData = [ + { + format: "spki", + data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255, 97]), + }, + { + format: "jwk", + data: { + crv: "X25519", + d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E", + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "X25519", + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY", + kty: "OKP" + }, + }, +]; + +// Removed just the last byte. +var badKeyLengthData = [ + { + format: "spki", + data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255]), + }, + { + format: "jwk", + data: { + crv: "X25519", + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lw", + kty: "OKP" + } + }, + { + format: "jwk", + data: { + crv: "X25519", + d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2", + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY", + kty: "OKP" + }, + }, +]; + +var missingJWKFieldKeyData = [ + { + param: "x", + data: { + crv: "X25519", + d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E", + kty: "OKP" + }, + }, + { + param: "kty", + data: { + crv: "X25519", + d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E", + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY", + }, + }, + { + param: "crv", + data: { + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY", + kty: "OKP" + }, + } +]; + +// The public key doesn't match the private key. +var invalidJWKKeyData = [ + { + crv: "X25519", + d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E", + x: "hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo", + kty: "OKP" + }, +]; + +run_test(["X25519"]); diff --git a/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.https.any.js b/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.https.any.js new file mode 100644 index 00000000000000..9e3b05c48ad55f --- /dev/null +++ b/test/fixtures/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.https.any.js @@ -0,0 +1,103 @@ +// META: title=WebCryptoAPI: importKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=okp_importKey_failures.js + +// Setup: define the correct behaviors that should be sought, and create +// helper functions that generate all possible test parameters for +// different situations. +var validKeyData = [ + { + format: "spki", + data: new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146, 158]), + }, + { + format: "jwk", + data: { + crv: "X448", + d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4", + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "X448", + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8", + kty: "OKP" + }, + }, +]; + +// Removed just the last byte. +var badKeyLengthData = [ + { + format: "spki", + data: new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146]), + }, + { + format: "jwk", + data: { + crv: "X448", + d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp", + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "X448", + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm", + kty: "OKP" + }, + }, +]; + +var missingJWKFieldKeyData = [ + { + param: "x", + data: { + crv: "X448", + d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4", + kty: "OKP" + } + }, + { + param: "kty", + data: { + crv: "X448", + d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4", + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8", + } + }, + { + param: "crv", + data: { + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8", + kty: "OKP" + } + } +]; + +// The public key doesn't match the private key. +var invalidJWKKeyData = [ + { + + crv: "X448", + kty: "OKP", + d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4", + x: "mwj3zDG34+Z9ItWuoSEHSic70rg94Jxj+qc9LCLF2bvINmRyQdlT1AxbEtqIEg1TF3+A5TLEH6A", + }, +]; + +run_test(["X448"]); diff --git a/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa.js b/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa.js index 6133a7ecba290c..6bf662adcc547f 100644 --- a/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa.js +++ b/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa.js @@ -10,6 +10,7 @@ function run_test() { // Source file [algorithm_name]_vectors.js provides the getTestVectors method // for the algorithm that drives these tests. var testVectors = getTestVectors(); + var invalidTestVectors = getInvalidTestVectors(); // Test verification first, because signing tests rely on that working testVectors.forEach(function(vector) { @@ -407,6 +408,32 @@ function run_test() { all_promises.push(promise); }); + // Test invalid signatures + invalidTestVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) + .then(function(is_verified) { + assert_false(is_verified, "Signature unexpectedly verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification"); + }); + + all_promises.push(promise); + }); promise_test(function() { return Promise.all(all_promises) diff --git a/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa_vectors.js b/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa_vectors.js index e605821aebf921..2d1fb6d5c9e80b 100644 --- a/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa_vectors.js +++ b/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa_vectors.js @@ -57,28 +57,6 @@ function getTestVectors() { } } - // Old ASN.1 signatures below. - // var signatures = { - // "P-256": { - // "SHA-1": new Uint8Array([48, 70, 2, 33, 0, 189, 178, 29, 63, 162, 177, 41, 146, 224, 212, 75, 195, 12, 201, 193, 68, 61, 21, 122, 25, 40, 54, 22, 203, 197, 247, 160, 97, 3, 157, 35, 146, 2, 33, 0, 202, 253, 208, 131, 220, 167, 213, 121, 60, 56, 76, 111, 93, 197, 64, 54, 149, 82, 23, 255, 65, 206, 208, 154, 16, 52, 250, 3, 135, 178, 223, 248]), - // "SHA-256": new Uint8Array([48, 68, 2, 32, 91, 78, 119, 119, 168, 102, 87, 56, 106, 33, 140, 190, 53, 232, 207, 81, 251, 156, 33, 85, 156, 6, 1, 183, 61, 254, 248, 113, 89, 191, 223, 202, 2, 32, 9, 130, 207, 194, 45, 48, 4, 134, 19, 133, 121, 124, 93, 141, 29, 63, 26, 0, 167, 132, 123, 80, 240, 184, 69, 182, 18, 111, 211, 211, 139, 209]), - // "SHA-384": new Uint8Array([48, 69, 2, 32, 62, 124, 63, 100, 198, 132, 82, 37, 86, 53, 94, 121, 230, 167, 204, 146, 92, 56, 129, 66, 185, 242, 140, 181, 218, 239, 217, 133, 15, 166, 13, 86, 2, 33, 0, 164, 128, 5, 101, 173, 76, 227, 174, 140, 27, 28, 83, 80, 176, 202, 44, 0, 137, 37, 16, 150, 14, 29, 149, 22, 134, 1, 2, 45, 15, 91, 154]), - // "SHA-512": new Uint8Array([48, 70, 2, 33, 0, 163, 149, 177, 250, 180, 46, 8, 35, 168, 219, 191, 25, 152, 174, 171, 100, 155, 171, 41, 170, 10, 113, 108, 160, 26, 11, 161, 69, 216, 74, 105, 155, 2, 33, 0, 236, 60, 103, 71, 26, 48, 70, 157, 54, 252, 27, 92, 152, 227, 103, 164, 153, 71, 71, 155, 103, 109, 38, 163, 158, 118, 238, 66, 50, 43, 29, 14]) - // }, - // "P-384": { - // "SHA-1": new Uint8Array([48, 100, 2, 48, 95, 88, 156, 202, 5, 12, 93, 174, 109, 126, 105, 41, 101, 6, 111, 143, 36, 14, 7, 57, 84, 139, 59, 112, 224, 57, 250, 236, 77, 184, 59, 102, 21, 149, 236, 134, 202, 147, 140, 244, 27, 204, 55, 75, 109, 245, 40, 218, 2, 48, 25, 244, 151, 221, 217, 106, 152, 238, 40, 59, 188, 50, 235, 147, 226, 44, 121, 16, 69, 231, 204, 59, 42, 174, 23, 80, 130, 170, 204, 34, 208, 154, 135, 143, 164, 94, 62, 226, 14, 100, 213, 229, 40, 176, 31, 148, 125, 75]), - // "SHA-256": new Uint8Array([48, 102, 2, 49, 0, 171, 16, 188, 253, 115, 108, 16, 69, 39, 187, 21, 188, 22, 86, 146, 2, 212, 145, 7, 120, 218, 186, 149, 139, 205, 55, 114, 208, 25, 183, 127, 2, 198, 234, 151, 193, 94, 12, 173, 170, 234, 130, 83, 193, 214, 110, 108, 72, 2, 49, 0, 136, 132, 142, 128, 157, 111, 141, 240, 49, 203, 203, 32, 121, 165, 57, 138, 81, 95, 64, 235, 251, 241, 59, 203, 214, 169, 17, 153, 112, 115, 91, 51, 66, 206, 172, 143, 39, 0, 217, 68, 242, 172, 86, 155, 174, 24, 39, 155]), - // "SHA-384": new Uint8Array([48, 102, 2, 49, 0, 227, 80, 5, 74, 3, 89, 195, 243, 249, 127, 97, 9, 62, 159, 116, 170, 52, 181, 161, 160, 213, 16, 10, 137, 120, 40, 244, 151, 155, 52, 2, 111, 41, 199, 65, 146, 146, 121, 176, 101, 240, 37, 147, 163, 92, 102, 70, 79, 2, 49, 0, 223, 182, 48, 0, 17, 216, 189, 37, 249, 104, 74, 195, 177, 87, 106, 14, 127, 86, 0, 139, 238, 6, 13, 130, 146, 12, 26, 166, 204, 169, 194, 27, 81, 170, 212, 2, 128, 235, 59, 159, 120, 79, 141, 151, 188, 132, 170, 70]), - // "SHA-512": new Uint8Array([48, 102, 2, 49, 0, 188, 136, 210, 146, 118, 251, 132, 224, 144, 121, 109, 86, 162, 216, 12, 148, 108, 169, 42, 79, 32, 152, 167, 20, 173, 176, 28, 67, 219, 93, 52, 167, 76, 140, 102, 244, 118, 146, 193, 134, 116, 26, 83, 43, 230, 241, 215, 135, 2, 49, 0, 178, 120, 154, 88, 189, 55, 9, 240, 26, 169, 201, 53, 83, 207, 11, 6, 83, 54, 194, 126, 249, 188, 189, 32, 88, 190, 228, 166, 66, 104, 103, 243, 64, 214, 153, 84, 80, 175, 20, 205, 9, 85, 74, 233, 90, 184, 240, 153]) - // }, - // "P-521": { - // "SHA-1": new Uint8Array([48, 129, 136, 2, 66, 1, 0, 159, 229, 63, 6, 27, 187, 208, 6, 90, 246, 116, 10, 87, 207, 237, 166, 143, 68, 223, 98, 232, 90, 95, 143, 20, 240, 164, 112, 19, 199, 4, 203, 196, 231, 179, 203, 229, 64, 51, 58, 224, 124, 97, 41, 235, 202, 28, 201, 52, 61, 76, 166, 233, 197, 247, 58, 37, 115, 146, 150, 142, 108, 176, 94, 2, 66, 1, 4, 164, 11, 249, 164, 172, 86, 59, 39, 111, 61, 210, 100, 176, 168, 243, 146, 236, 28, 21, 25, 97, 28, 56, 201, 159, 24, 97, 217, 178, 5, 13, 221, 64, 6, 39, 168, 54, 129, 3, 86, 157, 104, 87, 241, 92, 158, 142, 170, 27, 126, 138, 255, 44, 33, 161, 49, 192, 230, 186, 70, 42, 189, 124, 5]), - // "SHA-256": new Uint8Array([48, 129, 134, 2, 65, 115, 189, 109, 44, 118, 67, 34, 176, 16, 126, 246, 157, 34, 188, 209, 65, 231, 207, 180, 139, 53, 97, 110, 157, 19, 55, 35, 134, 90, 160, 20, 252, 130, 210, 179, 22, 76, 3, 142, 212, 71, 48, 251, 64, 18, 148, 199, 234, 163, 193, 120, 13, 153, 63, 174, 253, 58, 34, 130, 88, 138, 194, 248, 173, 53, 2, 65, 63, 0, 229, 139, 245, 33, 197, 245, 98, 139, 59, 87, 144, 16, 220, 183, 237, 125, 136, 134, 143, 146, 195, 0, 209, 105, 217, 20, 121, 76, 64, 87, 232, 86, 87, 136, 117, 237, 39, 83, 248, 3, 50, 236, 152, 121, 37, 116, 93, 91, 241, 235, 152, 95, 177, 217, 45, 247, 66, 193, 248, 131, 205, 132, 74]), - // "SHA-384": new Uint8Array([48, 129, 136, 2, 66, 0, 252, 248, 24, 253, 24, 36, 120, 84, 72, 47, 246, 13, 78, 112, 200, 131, 7, 131, 73, 235, 36, 93, 54, 219, 233, 242, 85, 1, 198, 187, 17, 17, 109, 13, 47, 204, 137, 224, 17, 6, 225, 178, 133, 98, 248, 53, 151, 33, 230, 160, 42, 208, 30, 230, 154, 108, 227, 123, 216, 215, 35, 179, 17, 91, 187, 2, 66, 1, 110, 43, 180, 40, 222, 59, 177, 3, 70, 177, 175, 118, 222, 31, 1, 46, 196, 237, 187, 15, 96, 241, 216, 136, 195, 194, 45, 163, 194, 92, 159, 179, 101, 194, 90, 141, 78, 28, 31, 199, 233, 228, 180, 223, 23, 171, 62, 247, 157, 62, 126, 90, 198, 132, 197, 34, 140, 227, 79, 190, 153, 137, 225, 226, 32]), - // "SHA-512": new Uint8Array([48, 129, 136, 2, 66, 0, 228, 69, 122, 14, 172, 82, 52, 181, 42, 214, 42, 107, 227, 154, 253, 177, 145, 236, 231, 251, 71, 46, 202, 46, 59, 63, 76, 195, 63, 130, 8, 50, 116, 179, 181, 203, 234, 27, 203, 55, 188, 239, 122, 107, 167, 163, 190, 141, 174, 35, 22, 176, 173, 157, 212, 49, 21, 69, 72, 100, 78, 131, 147, 57, 223, 2, 66, 1, 107, 241, 89, 194, 8, 164, 44, 33, 11, 173, 236, 115, 153, 16, 90, 155, 164, 247, 232, 18, 226, 223, 62, 75, 246, 178, 66, 176, 51, 74, 161, 74, 76, 14, 227, 217, 19, 114, 36, 76, 168, 151, 191, 20, 58, 179, 162, 205, 140, 156, 227, 88, 59, 161, 245, 61, 170, 211, 254, 99, 120, 17, 174, 175, 52]) - // } - // }; - var vectors = []; ["P-256", "P-384", "P-521"].forEach(function(curveName) { ["SHA-1", "SHA-256", "SHA-384", "SHA-512"].forEach(function(hashName) { @@ -103,3 +81,57 @@ function getTestVectors() { return vectors; } + +// Additional test vectors, using the same format as getTestVectors, but the +// signatures are invalid. +function getInvalidTestVectors() { + var vectors = [ + { + name: "The signature was truncated by 1 byte", + publicKeyBuffer: new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 156, 176, 207, 105, 48, 61, 175, 199, 97, 212, 228, 104, 123, 78, 207, 3, 158, 109, 52, 171, 150, 74, 248, 8, 16, 216, 213, 88, 164, 168, 214, 247, 45, 81, 35, 58, 23, 136, 146, 10, 134, 238, 8, 161, 150, 44, 121, 239, 163, 23, 251, 120, 121, 226, 151, 218, 210, 20, 109, 185, 149, 250, 28, 120]), + publicKeyFormat: "spki", + publicKey: null, + algorithmName: "ECDSA", + namedCurve: "P-256", + hashName: "SHA-512", + plaintext: new Uint8Array([110, 41, 50, 21, 51, 1, 164, 238, 246, 128, 230, 66, 137, 41, 173, 174, 152, 140, 16, 141, 102, 138, 49, 255, 85, 208, 72, 153, 71, 215, 95, 248, 26, 70, 191, 137, 232, 77, 100, 1, 240, 35, 190, 110, 135, 104, 143, 188, 215, 132, 215, 133, 202, 132, 103, 53, 82, 74, 203, 82, 208, 4, 82, 200, 64, 64, 164, 121, 231, 204, 51, 9, 54, 68, 29, 147, 187, 231, 34, 169, 67, 42, 110, 29, 177, 18, 181, 201, 64, 59, 16, 39, 44, 177, 52, 127, 214, 25, 212, 99, 247, 169, 210, 35, 173, 118, 253, 224, 109, 138, 104, 131, 80, 15, 184, 67, 35, 90, 191, 249, 142, 36, 27, 223, 181, 83, 140, 62]), + signature: new Uint8Array([75, 159, 145, 228, 40, 82, 135, 38, 26, 29, 28, 146, 60, 246, 25, 205, 82, 193, 117, 207, 231, 241, 190, 96, 165, 37, 140, 97, 3, 72, 186, 61, 40, 196, 95, 144, 29, 113, 196, 27, 41, 134, 56, 236, 13, 106, 133, 215, 252, 176, 195, 59, 191, 236, 90, 156, 129, 8, 70, 182, 57, 40, 154]), + }, + { + name: "The signature was made using SHA-512, however verification is being done using SHA-1.", + publicKeyBuffer: new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 156, 176, 207, 105, 48, 61, 175, 199, 97, 212, 228, 104, 123, 78, 207, 3, 158, 109, 52, 171, 150, 74, 248, 8, 16, 216, 213, 88, 164, 168, 214, 247, 45, 81, 35, 58, 23, 136, 146, 10, 134, 238, 8, 161, 150, 44, 121, 239, 163, 23, 251, 120, 121, 226, 151, 218, 210, 20, 109, 185, 149, 250, 28, 120]), + publicKeyFormat: "spki", + publicKey: null, + algorithmName: "ECDSA", + namedCurve: "P-256", + hashName: "SHA-1", + plaintext: new Uint8Array([110, 41, 50, 21, 51, 1, 164, 238, 246, 128, 230, 66, 137, 41, 173, 174, 152, 140, 16, 141, 102, 138, 49, 255, 85, 208, 72, 153, 71, 215, 95, 248, 26, 70, 191, 137, 232, 77, 100, 1, 240, 35, 190, 110, 135, 104, 143, 188, 215, 132, 215, 133, 202, 132, 103, 53, 82, 74, 203, 82, 208, 4, 82, 200, 64, 64, 164, 121, 231, 204, 51, 9, 54, 68, 29, 147, 187, 231, 34, 169, 67, 42, 110, 29, 177, 18, 181, 201, 64, 59, 16, 39, 44, 177, 52, 127, 214, 25, 212, 99, 247, 169, 210, 35, 173, 118, 253, 224, 109, 138, 104, 131, 80, 15, 184, 67, 35, 90, 191, 249, 142, 36, 27, 223, 181, 83, 140, 62]), + signature: new Uint8Array([75, 159, 145, 228, 40, 82, 135, 38, 26, 29, 28, 146, 60, 246, 25, 205, 82, 193, 117, 207, 231, 241, 190, 96, 165, 37, 140, 97, 3, 72, 186, 61, 40, 196, 95, 144, 29, 113, 196, 27, 41, 134, 56, 236, 13, 106, 133, 215, 252, 176, 195, 59, 191, 236, 90, 156, 129, 8, 70, 182, 57, 40, 154, 132]), + }, + { + name: "Excess padding", + publicKeyBuffer: new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 8, 116, 162, 224, 184, 255, 68, 143, 14, 84, 50, 30, 39, 244, 241, 230, 77, 6, 76, 222, 183, 210, 111, 69, 140, 50, 233, 48, 18, 15, 78, 87, 220, 133, 194, 105, 63, 151, 126, 237, 74, 142, 204, 141, 185, 129, 180, 217, 31, 105, 68, 109, 244, 244, 198, 245, 222, 25, 0, 63, 69, 248, 145, 208, 235, 205, 47, 255, 219, 92, 129, 192, 64, 232, 214, 153, 76, 67, 199, 254, 237, 185, 138, 74, 49, 237, 251, 53, 232, 154, 48, 1, 60, 59, 146, 103]), + publicKeyFormat: "spki", + publicKey: null, + algorithmName: "ECDSA", + namedCurve: "P-384", + hashName: "SHA-1", + plaintext: new Uint8Array([63, 7, 131, 165, 142, 102, 243, 210, 192, 204, 251, 95, 172, 63, 9, 219, 111, 134, 9, 208, 89, 43, 199, 127, 223, 254, 217, 207, 14, 19, 125, 38, 168, 103, 5, 118, 101, 243, 173, 129, 190, 235, 187, 219, 114, 61, 90, 71, 197, 128, 130, 143, 16, 247, 52, 122, 184, 169, 194, 77, 25, 95, 115, 109, 250, 230, 234, 227, 125, 136, 254, 59, 71, 53, 231, 198, 105, 168, 10, 193, 145, 62, 92, 36, 200, 193, 213, 205, 177, 95, 153, 79, 62, 194, 241, 199, 116, 117, 46, 20, 245, 150, 179, 140, 47, 191, 3, 118, 22, 214, 8, 36, 77, 61, 167, 212, 186, 223, 53, 19, 48, 249, 71, 224, 76, 195, 80, 231]), + // Each of r and s in this input is padded up to one extra byte. + signature: new Uint8Array([0, 141, 157, 62, 61, 11, 43, 40, 113, 234, 47, 3, 242, 123, 168, 105, 159, 33, 75, 232, 216, 117, 192, 215, 112, 176, 255, 241, 196, 206, 52, 31, 12, 131, 74, 193, 31, 158, 193, 43, 253, 184, 50, 11, 23, 36, 200, 194, 32, 0, 98, 21, 13, 251, 168, 230, 92, 12, 123, 231, 239, 129, 200, 114, 65, 210, 195, 122, 131, 194, 126, 179, 28, 204, 43, 60, 57, 87, 103, 10, 116, 76, 129, 190, 109, 116, 19, 64, 181, 24, 156, 192, 197, 71, 223, 129, 176, 210]), + }, + { + name: "Empty signature", + publicKeyBuffer: new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 8, 116, 162, 224, 184, 255, 68, 143, 14, 84, 50, 30, 39, 244, 241, 230, 77, 6, 76, 222, 183, 210, 111, 69, 140, 50, 233, 48, 18, 15, 78, 87, 220, 133, 194, 105, 63, 151, 126, 237, 74, 142, 204, 141, 185, 129, 180, 217, 31, 105, 68, 109, 244, 244, 198, 245, 222, 25, 0, 63, 69, 248, 145, 208, 235, 205, 47, 255, 219, 92, 129, 192, 64, 232, 214, 153, 76, 67, 199, 254, 237, 185, 138, 74, 49, 237, 251, 53, 232, 154, 48, 1, 60, 59, 146, 103]), + publicKeyFormat: "spki", + publicKey: null, + algorithmName: "ECDSA", + namedCurve: "P-384", + hashName: "SHA-1", + plaintext: new Uint8Array([63, 7, 131, 165, 142, 102, 243, 210, 192, 204, 251, 95, 172, 63, 9, 219, 111, 134, 9, 208, 89, 43, 199, 127, 223, 254, 217, 207, 14, 19, 125, 38, 168, 103, 5, 118, 101, 243, 173, 129, 190, 235, 187, 219, 114, 61, 90, 71, 197, 128, 130, 143, 16, 247, 52, 122, 184, 169, 194, 77, 25, 95, 115, 109, 250, 230, 234, 227, 125, 136, 254, 59, 71, 53, 231, 198, 105, 168, 10, 193, 145, 62, 92, 36, 200, 193, 213, 205, 177, 95, 153, 79, 62, 194, 241, 199, 116, 117, 46, 20, 245, 150, 179, 140, 47, 191, 3, 118, 22, 214, 8, 36, 77, 61, 167, 212, 186, 223, 53, 19, 48, 249, 71, 224, 76, 195, 80, 231]), + signature: new Uint8Array([]), + }, + ]; + + return vectors; +} diff --git a/test/fixtures/wpt/WebCryptoAPI/sign_verify/eddsa.js b/test/fixtures/wpt/WebCryptoAPI/sign_verify/eddsa.js index 0a2e638114ad19..d425fec2dc343e 100644 --- a/test/fixtures/wpt/WebCryptoAPI/sign_verify/eddsa.js +++ b/test/fixtures/wpt/WebCryptoAPI/sign_verify/eddsa.js @@ -354,6 +354,18 @@ function run_test() { .catch(function() {done();}) }, "setup"); + // Test that generated keys are valid for signing and verifying. + testVectors.forEach(function(vector) { + var algorithm = {name: vector.algorithmName}; + promise_test(async() => { + let key = await subtle.generateKey(algorithm, false, ["sign", "verify"]); + let signature = await subtle.sign(algorithm, key.privateKey, vector.data); + let isVerified = await subtle.verify(algorithm, key.publicKey, signature, vector.data); + assert_true(isVerified, "Verificaton failed."); + }, "Sign and verify using generated " + vector.algorithmName + " keys."); + }); + + // A test vector has all needed fields for signing and verifying, EXCEPT that the // key field may be null. This function replaces that null with the Correct // CryptoKey object. diff --git a/test/fixtures/wpt/WebCryptoAPI/sign_verify/rsa.js b/test/fixtures/wpt/WebCryptoAPI/sign_verify/rsa.js index 7a38089ec36343..3eb79fb0131d25 100644 --- a/test/fixtures/wpt/WebCryptoAPI/sign_verify/rsa.js +++ b/test/fixtures/wpt/WebCryptoAPI/sign_verify/rsa.js @@ -155,11 +155,17 @@ function run_test() { // Check for successful signing and verification. testVectors.forEach(function(vector) { + // RSA signing is deterministic with PKCS#1 v1.5, or PSS with zero-length salts. + const isDeterministic = !("saltLength" in vector.algorithm) || vector.algorithm.saltLength == 0; var promise = importVectorKeys(vector, ["verify"], ["sign"]) .then(function(vectors) { promise_test(function(test) { return subtle.sign(vector.algorithm, vector.privateKey, vector.plaintext) .then(function(signature) { + if (isDeterministic) { + // If deterministic, we can check the output matches. Otherwise, we can only check it verifies. + assert_true(equalBuffers(signature, vector.signature), "Signing did not give the expected output"); + } // Can we verify the new signature? return subtle.verify(vector.algorithm, vector.publicKey, signature, vector.plaintext) .then(function(is_verified) { @@ -173,10 +179,10 @@ function run_test() { // Will a second signing give us different signature? It should for PSS with non-empty salt return subtle.sign(vector.algorithm, vector.privateKey, vector.plaintext) .then(function(signature) { - if ("saltLength" in vector.algorithm && vector.algorithm.saltLength > 0) { - assert_false(equalBuffers(priorSignature, signature), "Two signings with a salt give different signatures") - } else { + if (isDeterministic) { assert_true(equalBuffers(priorSignature, signature), "Two signings with empty salt give same signature") + } else { + assert_false(equalBuffers(priorSignature, signature), "Two signings with a salt give different signatures") } }, function(err) { assert_unreached("second time verify error for test " + vector.name + ": '" + err.message + "'"); diff --git a/test/fixtures/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js b/test/fixtures/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js index f7e135c8532e14..65e640a258452f 100644 --- a/test/fixtures/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js +++ b/test/fixtures/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js @@ -58,7 +58,8 @@ } ]; - return Promise.all(parameters.map(function(params) { + // Using allSettled to skip unsupported test cases. + return Promise.allSettled(parameters.map(function(params) { return subtle.generateKey(params.generateParameters, true, ["wrapKey", "unwrapKey"]) .then(function(key) { var wrapper; @@ -81,6 +82,7 @@ {algorithm: {name: "RSA-OAEP", modulusLength: 1024, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["decrypt"], publicUsages: ["encrypt"]}, {algorithm: {name: "ECDSA", namedCurve: "P-256"}, privateUsages: ["sign"], publicUsages: ["verify"]}, {algorithm: {name: "ECDH", namedCurve: "P-256"}, privateUsages: ["deriveBits"], publicUsages: []}, + {algorithm: {name: "Ed25519" }, privateUsages: ["sign"], publicUsages: ["verify"]}, {algorithm: {name: "AES-CTR", length: 128}, usages: ["encrypt", "decrypt"]}, {algorithm: {name: "AES-CBC", length: 128}, usages: ["encrypt", "decrypt"]}, {algorithm: {name: "AES-GCM", length: 128}, usages: ["encrypt", "decrypt"]}, @@ -88,7 +90,8 @@ {algorithm: {name: "HMAC", length: 128, hash: "SHA-256"}, usages: ["sign", "verify"]} ]; - return Promise.all(parameters.map(function(params) { + // Using allSettled to skip unsupported test cases. + return Promise.allSettled(parameters.map(function(params) { var usages; if ("usages" in params) { usages = params.usages; @@ -380,6 +383,9 @@ case "ECDSA" : signParams = {name: "ECDSA", hash: "SHA-256"}; break; + case "Ed25519" : + signParams = {name: "Ed25519"}; + break; case "HMAC" : signParams = {name: "HMAC"}; break; @@ -416,7 +422,7 @@ if (expected.algorithm.name === "RSA-PSS" || expected.algorithm.name === "RSASSA-PKCS1-v1_5") { ["d","p","q","dp","dq","qi","oth"].forEach(function(field){ delete jwkExpectedKey[field]; }); } - if (expected.algorithm.name === "ECDSA") { + if (expected.algorithm.name === "ECDSA" || expected.algorithm.name === "Ed25519") { delete jwkExpectedKey["d"]; } jwkExpectedKey.key_ops = ["verify"]; diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index 2b6ae3401d3645..4c01db0b2de039 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -88,7 +88,7 @@ "path": "wasm/webapi" }, "WebCryptoAPI": { - "commit": "0042d42ee69baf05a4ac4f5745be9c3b92c04e25", + "commit": "21ccdcd8143d494024639c9e53bad565c9733831", "path": "WebCryptoAPI" }, "webidl/ecmascript-binding/es-exceptions": { diff --git a/test/wpt/status/WebCryptoAPI.json b/test/wpt/status/WebCryptoAPI.json index 9f101f6cdd92c9..f6f0e154c6b9fa 100644 --- a/test/wpt/status/WebCryptoAPI.json +++ b/test/wpt/status/WebCryptoAPI.json @@ -7,5 +7,153 @@ }, "idlharness.https.any.js": { "skip": "Various non-IDL-compliant things" + }, + "import_export/okp_importKey_failures_Ed25519.https.any.js": { + "fail": { + "expected": [ + "Bad key length: importKey(spki, {name: Ed25519}, true, [verify])", + "Bad key length: importKey(spki, {name: Ed25519}, false, [verify])", + "Bad key length: importKey(spki, {name: Ed25519}, true, [verify, verify])", + "Bad key length: importKey(spki, {name: Ed25519}, false, [verify, verify])", + "Bad key length: importKey(pkcs8, {name: Ed25519}, true, [sign])", + "Bad key length: importKey(pkcs8, {name: Ed25519}, false, [sign])", + "Bad key length: importKey(pkcs8, {name: Ed25519}, true, [sign, sign])", + "Bad key length: importKey(pkcs8, {name: Ed25519}, false, [sign, sign])", + "Bad key length: importKey(jwk(private), {name: Ed25519}, true, [sign])", + "Bad key length: importKey(jwk(private), {name: Ed25519}, false, [sign])", + "Bad key length: importKey(jwk(private), {name: Ed25519}, true, [sign, sign])", + "Bad key length: importKey(jwk(private), {name: Ed25519}, false, [sign, sign])", + "Bad key length: importKey(jwk (public) , {name: Ed25519}, true, [verify])", + "Bad key length: importKey(jwk (public) , {name: Ed25519}, false, [verify])", + "Bad key length: importKey(jwk (public) , {name: Ed25519}, true, [verify, verify])", + "Bad key length: importKey(jwk (public) , {name: Ed25519}, false, [verify, verify])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed25519}, true, [sign])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed25519}, false, [sign])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed25519}, true, [sign, sign])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed25519}, false, [sign, sign])", + "Invalid key pair: importKey(jwk(private), {name: Ed25519}, true, [sign])", + "Invalid key pair: importKey(jwk(private), {name: Ed25519}, true, [sign, sign])" + ] + } + }, + "import_export/okp_importKey_failures_Ed448.https.any.js": { + "fail": { + "expected": [ + "Bad key length: importKey(spki, {name: Ed448}, true, [verify])", + "Bad key length: importKey(spki, {name: Ed448}, false, [verify])", + "Bad key length: importKey(spki, {name: Ed448}, true, [verify, verify])", + "Bad key length: importKey(spki, {name: Ed448}, false, [verify, verify])", + "Bad key length: importKey(pkcs8, {name: Ed448}, true, [sign])", + "Bad key length: importKey(pkcs8, {name: Ed448}, false, [sign])", + "Bad key length: importKey(pkcs8, {name: Ed448}, true, [sign, sign])", + "Bad key length: importKey(pkcs8, {name: Ed448}, false, [sign, sign])", + "Bad key length: importKey(jwk(private), {name: Ed448}, true, [sign])", + "Bad key length: importKey(jwk(private), {name: Ed448}, false, [sign])", + "Bad key length: importKey(jwk(private), {name: Ed448}, true, [sign, sign])", + "Bad key length: importKey(jwk(private), {name: Ed448}, false, [sign, sign])", + "Bad key length: importKey(jwk (public) , {name: Ed448}, true, [verify])", + "Bad key length: importKey(jwk (public) , {name: Ed448}, false, [verify])", + "Bad key length: importKey(jwk (public) , {name: Ed448}, true, [verify, verify])", + "Bad key length: importKey(jwk (public) , {name: Ed448}, false, [verify, verify])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed448}, true, [sign])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed448}, false, [sign])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed448}, true, [sign, sign])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed448}, false, [sign, sign])", + "Invalid key pair: importKey(jwk(private), {name: Ed448}, true, [sign])", + "Invalid key pair: importKey(jwk(private), {name: Ed448}, true, [sign, sign])" + ] + } + }, + "import_export/okp_importKey_failures_X25519.https.any.js": { + "fail": { + "expected": [ + "Bad usages: importKey(spki, {name: X25519}, true, [deriveKey])", + "Bad usages: importKey(spki, {name: X25519}, false, [deriveKey])", + "Bad usages: importKey(spki, {name: X25519}, true, [deriveBits])", + "Bad usages: importKey(spki, {name: X25519}, false, [deriveBits])", + "Bad usages: importKey(jwk (public) , {name: X25519}, true, [deriveKey])", + "Bad usages: importKey(jwk (public) , {name: X25519}, false, [deriveKey])", + "Bad usages: importKey(jwk (public) , {name: X25519}, true, [deriveBits])", + "Bad usages: importKey(jwk (public) , {name: X25519}, false, [deriveBits])", + "Bad key length: importKey(spki, {name: X25519}, true, [])", + "Bad key length: importKey(spki, {name: X25519}, false, [])", + "Bad key length: importKey(pkcs8, {name: X25519}, true, [deriveKey])", + "Bad key length: importKey(pkcs8, {name: X25519}, false, [deriveKey])", + "Bad key length: importKey(pkcs8, {name: X25519}, true, [deriveBits, deriveKey])", + "Bad key length: importKey(pkcs8, {name: X25519}, false, [deriveBits, deriveKey])", + "Bad key length: importKey(pkcs8, {name: X25519}, true, [deriveBits])", + "Bad key length: importKey(pkcs8, {name: X25519}, false, [deriveBits])", + "Bad key length: importKey(pkcs8, {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(pkcs8, {name: X25519}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk (public) , {name: X25519}, true, [])", + "Bad key length: importKey(jwk (public) , {name: X25519}, false, [])", + "Bad key length: importKey(jwk(private), {name: X25519}, true, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X25519}, false, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X25519}, true, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X25519}, false, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X25519}, true, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X25519}, false, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk(private), {name: X25519}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, true, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, false, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, true, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, false, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, true, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, false, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveBits, deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])" + ] + } + }, + "import_export/okp_importKey_failures_X448.https.any.js": { + "fail": { + "expected": [ + "Bad usages: importKey(spki, {name: X448}, true, [deriveKey])", + "Bad usages: importKey(spki, {name: X448}, false, [deriveKey])", + "Bad usages: importKey(spki, {name: X448}, true, [deriveBits])", + "Bad usages: importKey(spki, {name: X448}, false, [deriveBits])", + "Bad usages: importKey(jwk (public) , {name: X448}, true, [deriveKey])", + "Bad usages: importKey(jwk (public) , {name: X448}, false, [deriveKey])", + "Bad usages: importKey(jwk (public) , {name: X448}, true, [deriveBits])", + "Bad usages: importKey(jwk (public) , {name: X448}, false, [deriveBits])", + "Bad key length: importKey(spki, {name: X448}, true, [])", + "Bad key length: importKey(spki, {name: X448}, false, [])", + "Bad key length: importKey(pkcs8, {name: X448}, true, [deriveKey])", + "Bad key length: importKey(pkcs8, {name: X448}, false, [deriveKey])", + "Bad key length: importKey(pkcs8, {name: X448}, true, [deriveBits, deriveKey])", + "Bad key length: importKey(pkcs8, {name: X448}, false, [deriveBits, deriveKey])", + "Bad key length: importKey(pkcs8, {name: X448}, true, [deriveBits])", + "Bad key length: importKey(pkcs8, {name: X448}, false, [deriveBits])", + "Bad key length: importKey(pkcs8, {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk (public) , {name: X448}, true, [])", + "Bad key length: importKey(jwk (public) , {name: X448}, false, [])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])" + ] + } } } From de2b6b97b91e491950fb86f66055a11822cd9edd Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 21 Nov 2022 23:09:57 +0100 Subject: [PATCH 07/97] crypto: ensure "x" is present when importing private CFRG webcrypto keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/45569 Reviewed-By: Antoine du Hamel Reviewed-By: Tobias Nießen --- lib/internal/crypto/cfrg.js | 4 ++++ test/wpt/status/WebCryptoAPI.json | 24 ------------------------ 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/lib/internal/crypto/cfrg.js b/lib/internal/crypto/cfrg.js index a4b57825f3702e..f26444a2ed2489 100644 --- a/lib/internal/crypto/cfrg.js +++ b/lib/internal/crypto/cfrg.js @@ -275,6 +275,10 @@ async function cfrgImportKey( } } + if (!isPublic && typeof keyData.x !== 'string') { + throw lazyDOMException('Invalid JWK keyData', 'DataError'); + } + verifyAcceptableCfrgKeyUse( name, isPublic ? 'public' : 'private', diff --git a/test/wpt/status/WebCryptoAPI.json b/test/wpt/status/WebCryptoAPI.json index f6f0e154c6b9fa..6676317874c4b3 100644 --- a/test/wpt/status/WebCryptoAPI.json +++ b/test/wpt/status/WebCryptoAPI.json @@ -27,10 +27,6 @@ "Bad key length: importKey(jwk (public) , {name: Ed25519}, false, [verify])", "Bad key length: importKey(jwk (public) , {name: Ed25519}, true, [verify, verify])", "Bad key length: importKey(jwk (public) , {name: Ed25519}, false, [verify, verify])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed25519}, true, [sign])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed25519}, false, [sign])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed25519}, true, [sign, sign])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed25519}, false, [sign, sign])", "Invalid key pair: importKey(jwk(private), {name: Ed25519}, true, [sign])", "Invalid key pair: importKey(jwk(private), {name: Ed25519}, true, [sign, sign])" ] @@ -55,10 +51,6 @@ "Bad key length: importKey(jwk (public) , {name: Ed448}, false, [verify])", "Bad key length: importKey(jwk (public) , {name: Ed448}, true, [verify, verify])", "Bad key length: importKey(jwk (public) , {name: Ed448}, false, [verify, verify])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed448}, true, [sign])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed448}, false, [sign])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed448}, true, [sign, sign])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: Ed448}, false, [sign, sign])", "Invalid key pair: importKey(jwk(private), {name: Ed448}, true, [sign])", "Invalid key pair: importKey(jwk(private), {name: Ed448}, true, [sign, sign])" ] @@ -95,14 +87,6 @@ "Bad key length: importKey(jwk(private), {name: X25519}, false, [deriveBits])", "Bad key length: importKey(jwk(private), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Bad key length: importKey(jwk(private), {name: X25519}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, true, [deriveKey])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, false, [deriveKey])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, true, [deriveBits, deriveKey])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, false, [deriveBits, deriveKey])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, true, [deriveBits])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, false, [deriveBits])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X25519}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveKey])", "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveBits, deriveKey])", "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveBits])", @@ -141,14 +125,6 @@ "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", "Bad key length: importKey(jwk (public) , {name: X448}, true, [])", "Bad key length: importKey(jwk (public) , {name: X448}, false, [])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey])", "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits])", From 40037b4e791a7a13fb65d5d08241ba2ca1f27c8b Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 21 Nov 2022 23:14:06 +0100 Subject: [PATCH 08/97] crypto: fix X25519 and X448 webcrypto public CryptoKey usages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/45569 Reviewed-By: Antoine du Hamel Reviewed-By: Tobias Nießen --- lib/internal/crypto/cfrg.js | 9 ++++++++- test/parallel/test-webcrypto-derivebits-cfrg.js | 8 ++++---- test/parallel/test-webcrypto-derivekey-cfrg.js | 12 ++++++------ .../test-webcrypto-export-import-cfrg.js | 12 ++++++------ test/wpt/status/WebCryptoAPI.json | 16 ---------------- 5 files changed, 24 insertions(+), 33 deletions(-) diff --git a/lib/internal/crypto/cfrg.js b/lib/internal/crypto/cfrg.js index f26444a2ed2489..125bc92d04ab6f 100644 --- a/lib/internal/crypto/cfrg.js +++ b/lib/internal/crypto/cfrg.js @@ -53,7 +53,14 @@ function verifyAcceptableCfrgKeyUse(name, type, usages) { case 'X25519': // Fall through case 'X448': - checkSet = ['deriveKey', 'deriveBits']; + switch (type) { + case 'private': + checkSet = ['deriveKey', 'deriveBits']; + break; + case 'public': + checkSet = []; + break; + } break; case 'Ed25519': // Fall through diff --git a/test/parallel/test-webcrypto-derivebits-cfrg.js b/test/parallel/test-webcrypto-derivebits-cfrg.js index 2233d1a2d274c7..1324a5fecc4c87 100644 --- a/test/parallel/test-webcrypto-derivebits-cfrg.js +++ b/test/parallel/test-webcrypto-derivebits-cfrg.js @@ -52,7 +52,7 @@ async function prepareKeys() { Buffer.from(spki, 'hex'), { name }, true, - ['deriveKey', 'deriveBits']), + []), ]); keys[name] = { privateKey, @@ -180,7 +180,7 @@ async function prepareKeys() { name: 'X448', public: keys.X448.publicKey }, keys.X448.publicKey, null), { - message: /baseKey must be a private key/ + name: 'InvalidAccessError' }); } @@ -190,7 +190,7 @@ async function prepareKeys() { name: 'X448', public: keys.X448.privateKey }, keys.X448.publicKey, null), { - message: /algorithm\.public must be a public key/ + name: 'InvalidAccessError' }); } @@ -207,7 +207,7 @@ async function prepareKeys() { name: 'X448', public: key }, keys.X448.publicKey, null), { - message: /algorithm\.public must be a public key/ + name: 'InvalidAccessError' }); } })().then(common.mustCall()); diff --git a/test/parallel/test-webcrypto-derivekey-cfrg.js b/test/parallel/test-webcrypto-derivekey-cfrg.js index ddf51626a89b4b..90289c98efd5d5 100644 --- a/test/parallel/test-webcrypto-derivekey-cfrg.js +++ b/test/parallel/test-webcrypto-derivekey-cfrg.js @@ -51,7 +51,7 @@ async function prepareKeys() { Buffer.from(spki, 'hex'), { name }, true, - ['deriveKey', 'deriveBits']), + []), ]); keys[name] = { privateKey, @@ -150,20 +150,20 @@ async function prepareKeys() { }, keys.X448.publicKey, ...otherArgs), - { message: /baseKey must be a private key/ }); + { name: 'InvalidAccessError' }); } { - // Base key is not a private key + // Public is not a public key await assert.rejects( subtle.deriveKey( { name: 'X448', public: keys.X448.privateKey }, - keys.X448.publicKey, + keys.X448.privateKey, ...otherArgs), - { message: /algorithm\.public must be a public key/ }); + { name: 'InvalidAccessError' }); } { @@ -183,6 +183,6 @@ async function prepareKeys() { }, keys.X448.publicKey, ...otherArgs), - { message: /algorithm\.public must be a public key/ }); + { name: 'InvalidAccessError' }); } })().then(common.mustCall()); diff --git a/test/parallel/test-webcrypto-export-import-cfrg.js b/test/parallel/test-webcrypto-export-import-cfrg.js index 6d162ac61c2e30..af6a7956e6716a 100644 --- a/test/parallel/test-webcrypto-export-import-cfrg.js +++ b/test/parallel/test-webcrypto-export-import-cfrg.js @@ -315,19 +315,19 @@ async function testImportRaw({ name, publicUsages }) { const rsaPrivate = crypto.createPrivateKey( fixtures.readKey('rsa_private_2048.pem')); - for (const [name, [publicUsage, privateUsage]] of Object.entries({ - 'Ed25519': ['verify', 'sign'], - 'X448': ['deriveBits', 'deriveBits'], - })) { + for (const [name, publicUsages, privateUsages] of [ + ['Ed25519', ['verify'], ['sign']], + ['X448', [], ['deriveBits']], + ]) { assert.rejects(subtle.importKey( 'spki', rsaPublic.export({ format: 'der', type: 'spki' }), { name }, - true, [publicUsage]), { message: /Invalid key type/ }); + true, publicUsages), { message: /Invalid key type/ }); assert.rejects(subtle.importKey( 'pkcs8', rsaPrivate.export({ format: 'der', type: 'pkcs8' }), { name }, - true, [privateUsage]), { message: /Invalid key type/ }); + true, privateUsages), { message: /Invalid key type/ }); } } diff --git a/test/wpt/status/WebCryptoAPI.json b/test/wpt/status/WebCryptoAPI.json index 6676317874c4b3..365b262c0d1552 100644 --- a/test/wpt/status/WebCryptoAPI.json +++ b/test/wpt/status/WebCryptoAPI.json @@ -59,14 +59,6 @@ "import_export/okp_importKey_failures_X25519.https.any.js": { "fail": { "expected": [ - "Bad usages: importKey(spki, {name: X25519}, true, [deriveKey])", - "Bad usages: importKey(spki, {name: X25519}, false, [deriveKey])", - "Bad usages: importKey(spki, {name: X25519}, true, [deriveBits])", - "Bad usages: importKey(spki, {name: X25519}, false, [deriveBits])", - "Bad usages: importKey(jwk (public) , {name: X25519}, true, [deriveKey])", - "Bad usages: importKey(jwk (public) , {name: X25519}, false, [deriveKey])", - "Bad usages: importKey(jwk (public) , {name: X25519}, true, [deriveBits])", - "Bad usages: importKey(jwk (public) , {name: X25519}, false, [deriveBits])", "Bad key length: importKey(spki, {name: X25519}, true, [])", "Bad key length: importKey(spki, {name: X25519}, false, [])", "Bad key length: importKey(pkcs8, {name: X25519}, true, [deriveKey])", @@ -97,14 +89,6 @@ "import_export/okp_importKey_failures_X448.https.any.js": { "fail": { "expected": [ - "Bad usages: importKey(spki, {name: X448}, true, [deriveKey])", - "Bad usages: importKey(spki, {name: X448}, false, [deriveKey])", - "Bad usages: importKey(spki, {name: X448}, true, [deriveBits])", - "Bad usages: importKey(spki, {name: X448}, false, [deriveBits])", - "Bad usages: importKey(jwk (public) , {name: X448}, true, [deriveKey])", - "Bad usages: importKey(jwk (public) , {name: X448}, false, [deriveKey])", - "Bad usages: importKey(jwk (public) , {name: X448}, true, [deriveBits])", - "Bad usages: importKey(jwk (public) , {name: X448}, false, [deriveBits])", "Bad key length: importKey(spki, {name: X448}, true, [])", "Bad key length: importKey(spki, {name: X448}, false, [])", "Bad key length: importKey(pkcs8, {name: X448}, true, [deriveKey])", From 9e2e3de6ce96758184ab4210bb41b2ef5ef8ccc0 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 21 Nov 2022 23:43:57 +0100 Subject: [PATCH 09/97] crypto: use DataError for webcrypto keyData import failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/45569 Reviewed-By: Antoine du Hamel Reviewed-By: Tobias Nießen --- lib/internal/crypto/cfrg.js | 32 ++++++++++++++++--------- lib/internal/crypto/ec.js | 40 ++++++++++++++++++++----------- lib/internal/crypto/rsa.js | 30 +++++++++++++++-------- test/wpt/status/WebCryptoAPI.json | 36 ---------------------------- 4 files changed, 67 insertions(+), 71 deletions(-) diff --git a/lib/internal/crypto/cfrg.js b/lib/internal/crypto/cfrg.js index 125bc92d04ab6f..c8b1b345867643 100644 --- a/lib/internal/crypto/cfrg.js +++ b/lib/internal/crypto/cfrg.js @@ -109,7 +109,7 @@ function createCFRGRawKey(name, keyData, isPublic) { const keyType = isPublic ? kKeyTypePublic : kKeyTypePrivate; if (!handle.initEDRaw(name, keyData, keyType)) { - throw lazyDOMException('Failure to generate key object'); + throw lazyDOMException('Invalid keyData', 'DataError'); } return isPublic ? new PublicKeyObject(handle) : new PrivateKeyObject(handle); @@ -220,20 +220,30 @@ async function cfrgImportKey( switch (format) { case 'spki': { verifyAcceptableCfrgKeyUse(name, 'public', usagesSet); - keyObject = createPublicKey({ - key: keyData, - format: 'der', - type: 'spki' - }); + try { + keyObject = createPublicKey({ + key: keyData, + format: 'der', + type: 'spki' + }); + } catch (err) { + throw lazyDOMException( + 'Invalid keyData', { name: 'DataError', cause: err }); + } break; } case 'pkcs8': { verifyAcceptableCfrgKeyUse(name, 'private', usagesSet); - keyObject = createPrivateKey({ - key: keyData, - format: 'der', - type: 'pkcs8' - }); + try { + keyObject = createPrivateKey({ + key: keyData, + format: 'der', + type: 'pkcs8' + }); + } catch (err) { + throw lazyDOMException( + 'Invalid keyData', { name: 'DataError', cause: err }); + } break; } case 'jwk': { diff --git a/lib/internal/crypto/ec.js b/lib/internal/crypto/ec.js index 8d70633b28b244..4359294d343be0 100644 --- a/lib/internal/crypto/ec.js +++ b/lib/internal/crypto/ec.js @@ -80,8 +80,12 @@ function verifyAcceptableEcKeyUse(name, type, usages) { function createECPublicKeyRaw(namedCurve, keyData) { const handle = new KeyObjectHandle(); keyData = getArrayBufferOrView(keyData, 'keyData'); - if (handle.initECRaw(kNamedCurveAliases[namedCurve], keyData)) - return new PublicKeyObject(handle); + + if (!handle.initECRaw(kNamedCurveAliases[namedCurve], keyData)) { + throw lazyDOMException('Invalid keyData', 'DataError'); + } + + return new PublicKeyObject(handle); } async function ecGenerateKey(algorithm, extractable, keyUsages) { @@ -176,20 +180,30 @@ async function ecImportKey( switch (format) { case 'spki': { verifyAcceptableEcKeyUse(name, 'public', usagesSet); - keyObject = createPublicKey({ - key: keyData, - format: 'der', - type: 'spki' - }); + try { + keyObject = createPublicKey({ + key: keyData, + format: 'der', + type: 'spki' + }); + } catch (err) { + throw lazyDOMException( + 'Invalid keyData', { name: 'DataError', cause: err }); + } break; } case 'pkcs8': { verifyAcceptableEcKeyUse(name, 'private', usagesSet); - keyObject = createPrivateKey({ - key: keyData, - format: 'der', - type: 'pkcs8' - }); + try { + keyObject = createPrivateKey({ + key: keyData, + format: 'der', + type: 'pkcs8' + }); + } catch (err) { + throw lazyDOMException( + 'Invalid keyData', { name: 'DataError', cause: err }); + } break; } case 'jwk': { @@ -246,8 +260,6 @@ async function ecImportKey( case 'raw': { verifyAcceptableEcKeyUse(name, 'public', usagesSet); keyObject = createECPublicKeyRaw(namedCurve, keyData); - if (keyObject === undefined) - throw lazyDOMException('Unable to import EC key', 'OperationError'); break; } } diff --git a/lib/internal/crypto/rsa.js b/lib/internal/crypto/rsa.js index 044d19374ba597..0ec169a116295e 100644 --- a/lib/internal/crypto/rsa.js +++ b/lib/internal/crypto/rsa.js @@ -245,20 +245,30 @@ async function rsaImportKey( switch (format) { case 'spki': { verifyAcceptableRsaKeyUse(algorithm.name, 'public', usagesSet); - keyObject = createPublicKey({ - key: keyData, - format: 'der', - type: 'spki' - }); + try { + keyObject = createPublicKey({ + key: keyData, + format: 'der', + type: 'spki' + }); + } catch (err) { + throw lazyDOMException( + 'Invalid keyData', { name: 'DataError', cause: err }); + } break; } case 'pkcs8': { verifyAcceptableRsaKeyUse(algorithm.name, 'private', usagesSet); - keyObject = createPrivateKey({ - key: keyData, - format: 'der', - type: 'pkcs8' - }); + try { + keyObject = createPrivateKey({ + key: keyData, + format: 'der', + type: 'pkcs8' + }); + } catch (err) { + throw lazyDOMException( + 'Invalid keyData', { name: 'DataError', cause: err }); + } break; } case 'jwk': { diff --git a/test/wpt/status/WebCryptoAPI.json b/test/wpt/status/WebCryptoAPI.json index 365b262c0d1552..7f61a1f0364cb5 100644 --- a/test/wpt/status/WebCryptoAPI.json +++ b/test/wpt/status/WebCryptoAPI.json @@ -11,14 +11,6 @@ "import_export/okp_importKey_failures_Ed25519.https.any.js": { "fail": { "expected": [ - "Bad key length: importKey(spki, {name: Ed25519}, true, [verify])", - "Bad key length: importKey(spki, {name: Ed25519}, false, [verify])", - "Bad key length: importKey(spki, {name: Ed25519}, true, [verify, verify])", - "Bad key length: importKey(spki, {name: Ed25519}, false, [verify, verify])", - "Bad key length: importKey(pkcs8, {name: Ed25519}, true, [sign])", - "Bad key length: importKey(pkcs8, {name: Ed25519}, false, [sign])", - "Bad key length: importKey(pkcs8, {name: Ed25519}, true, [sign, sign])", - "Bad key length: importKey(pkcs8, {name: Ed25519}, false, [sign, sign])", "Bad key length: importKey(jwk(private), {name: Ed25519}, true, [sign])", "Bad key length: importKey(jwk(private), {name: Ed25519}, false, [sign])", "Bad key length: importKey(jwk(private), {name: Ed25519}, true, [sign, sign])", @@ -35,14 +27,6 @@ "import_export/okp_importKey_failures_Ed448.https.any.js": { "fail": { "expected": [ - "Bad key length: importKey(spki, {name: Ed448}, true, [verify])", - "Bad key length: importKey(spki, {name: Ed448}, false, [verify])", - "Bad key length: importKey(spki, {name: Ed448}, true, [verify, verify])", - "Bad key length: importKey(spki, {name: Ed448}, false, [verify, verify])", - "Bad key length: importKey(pkcs8, {name: Ed448}, true, [sign])", - "Bad key length: importKey(pkcs8, {name: Ed448}, false, [sign])", - "Bad key length: importKey(pkcs8, {name: Ed448}, true, [sign, sign])", - "Bad key length: importKey(pkcs8, {name: Ed448}, false, [sign, sign])", "Bad key length: importKey(jwk(private), {name: Ed448}, true, [sign])", "Bad key length: importKey(jwk(private), {name: Ed448}, false, [sign])", "Bad key length: importKey(jwk(private), {name: Ed448}, true, [sign, sign])", @@ -59,16 +43,6 @@ "import_export/okp_importKey_failures_X25519.https.any.js": { "fail": { "expected": [ - "Bad key length: importKey(spki, {name: X25519}, true, [])", - "Bad key length: importKey(spki, {name: X25519}, false, [])", - "Bad key length: importKey(pkcs8, {name: X25519}, true, [deriveKey])", - "Bad key length: importKey(pkcs8, {name: X25519}, false, [deriveKey])", - "Bad key length: importKey(pkcs8, {name: X25519}, true, [deriveBits, deriveKey])", - "Bad key length: importKey(pkcs8, {name: X25519}, false, [deriveBits, deriveKey])", - "Bad key length: importKey(pkcs8, {name: X25519}, true, [deriveBits])", - "Bad key length: importKey(pkcs8, {name: X25519}, false, [deriveBits])", - "Bad key length: importKey(pkcs8, {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Bad key length: importKey(pkcs8, {name: X25519}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", "Bad key length: importKey(jwk (public) , {name: X25519}, true, [])", "Bad key length: importKey(jwk (public) , {name: X25519}, false, [])", "Bad key length: importKey(jwk(private), {name: X25519}, true, [deriveKey])", @@ -89,16 +63,6 @@ "import_export/okp_importKey_failures_X448.https.any.js": { "fail": { "expected": [ - "Bad key length: importKey(spki, {name: X448}, true, [])", - "Bad key length: importKey(spki, {name: X448}, false, [])", - "Bad key length: importKey(pkcs8, {name: X448}, true, [deriveKey])", - "Bad key length: importKey(pkcs8, {name: X448}, false, [deriveKey])", - "Bad key length: importKey(pkcs8, {name: X448}, true, [deriveBits, deriveKey])", - "Bad key length: importKey(pkcs8, {name: X448}, false, [deriveBits, deriveKey])", - "Bad key length: importKey(pkcs8, {name: X448}, true, [deriveBits])", - "Bad key length: importKey(pkcs8, {name: X448}, false, [deriveBits])", - "Bad key length: importKey(pkcs8, {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Bad key length: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey])", "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey])", "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", From 9cd106efdc93e3eb137d59466a1427691525b2ab Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 21 Nov 2022 23:33:14 +0100 Subject: [PATCH 10/97] crypto: use DataError for CFRG webcrypto raw and jwk import key checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/45569 Reviewed-By: Antoine du Hamel Reviewed-By: Tobias Nießen --- lib/internal/crypto/cfrg.js | 6 +++--- test/wpt/status/WebCryptoAPI.json | 36 ------------------------------- 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/lib/internal/crypto/cfrg.js b/lib/internal/crypto/cfrg.js index c8b1b345867643..9ce38c538e2d49 100644 --- a/lib/internal/crypto/cfrg.js +++ b/lib/internal/crypto/cfrg.js @@ -90,19 +90,19 @@ function createCFRGRawKey(name, keyData, isPublic) { case 'X25519': if (keyData.byteLength !== 32) { throw lazyDOMException( - `${name} raw keys must be exactly 32-bytes`); + `${name} raw keys must be exactly 32-bytes`, 'DataError'); } break; case 'Ed448': if (keyData.byteLength !== 57) { throw lazyDOMException( - `${name} raw keys must be exactly 57-bytes`); + `${name} raw keys must be exactly 57-bytes`, 'DataError'); } break; case 'X448': if (keyData.byteLength !== 56) { throw lazyDOMException( - `${name} raw keys must be exactly 56-bytes`); + `${name} raw keys must be exactly 56-bytes`, 'DataError'); } break; } diff --git a/test/wpt/status/WebCryptoAPI.json b/test/wpt/status/WebCryptoAPI.json index 7f61a1f0364cb5..adaa217cf202b1 100644 --- a/test/wpt/status/WebCryptoAPI.json +++ b/test/wpt/status/WebCryptoAPI.json @@ -11,14 +11,6 @@ "import_export/okp_importKey_failures_Ed25519.https.any.js": { "fail": { "expected": [ - "Bad key length: importKey(jwk(private), {name: Ed25519}, true, [sign])", - "Bad key length: importKey(jwk(private), {name: Ed25519}, false, [sign])", - "Bad key length: importKey(jwk(private), {name: Ed25519}, true, [sign, sign])", - "Bad key length: importKey(jwk(private), {name: Ed25519}, false, [sign, sign])", - "Bad key length: importKey(jwk (public) , {name: Ed25519}, true, [verify])", - "Bad key length: importKey(jwk (public) , {name: Ed25519}, false, [verify])", - "Bad key length: importKey(jwk (public) , {name: Ed25519}, true, [verify, verify])", - "Bad key length: importKey(jwk (public) , {name: Ed25519}, false, [verify, verify])", "Invalid key pair: importKey(jwk(private), {name: Ed25519}, true, [sign])", "Invalid key pair: importKey(jwk(private), {name: Ed25519}, true, [sign, sign])" ] @@ -27,14 +19,6 @@ "import_export/okp_importKey_failures_Ed448.https.any.js": { "fail": { "expected": [ - "Bad key length: importKey(jwk(private), {name: Ed448}, true, [sign])", - "Bad key length: importKey(jwk(private), {name: Ed448}, false, [sign])", - "Bad key length: importKey(jwk(private), {name: Ed448}, true, [sign, sign])", - "Bad key length: importKey(jwk(private), {name: Ed448}, false, [sign, sign])", - "Bad key length: importKey(jwk (public) , {name: Ed448}, true, [verify])", - "Bad key length: importKey(jwk (public) , {name: Ed448}, false, [verify])", - "Bad key length: importKey(jwk (public) , {name: Ed448}, true, [verify, verify])", - "Bad key length: importKey(jwk (public) , {name: Ed448}, false, [verify, verify])", "Invalid key pair: importKey(jwk(private), {name: Ed448}, true, [sign])", "Invalid key pair: importKey(jwk(private), {name: Ed448}, true, [sign, sign])" ] @@ -43,16 +27,6 @@ "import_export/okp_importKey_failures_X25519.https.any.js": { "fail": { "expected": [ - "Bad key length: importKey(jwk (public) , {name: X25519}, true, [])", - "Bad key length: importKey(jwk (public) , {name: X25519}, false, [])", - "Bad key length: importKey(jwk(private), {name: X25519}, true, [deriveKey])", - "Bad key length: importKey(jwk(private), {name: X25519}, false, [deriveKey])", - "Bad key length: importKey(jwk(private), {name: X25519}, true, [deriveBits, deriveKey])", - "Bad key length: importKey(jwk(private), {name: X25519}, false, [deriveBits, deriveKey])", - "Bad key length: importKey(jwk(private), {name: X25519}, true, [deriveBits])", - "Bad key length: importKey(jwk(private), {name: X25519}, false, [deriveBits])", - "Bad key length: importKey(jwk(private), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Bad key length: importKey(jwk(private), {name: X25519}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveKey])", "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveBits, deriveKey])", "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveBits])", @@ -63,16 +37,6 @@ "import_export/okp_importKey_failures_X448.https.any.js": { "fail": { "expected": [ - "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey])", - "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey])", - "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", - "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", - "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits])", - "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits])", - "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Bad key length: importKey(jwk (public) , {name: X448}, true, [])", - "Bad key length: importKey(jwk (public) , {name: X448}, false, [])", "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey])", "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits])", From d030963f37467126174335ea5d55db956c672077 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 21 Nov 2022 23:30:21 +0100 Subject: [PATCH 11/97] crypto: validate CFRG webcrypto JWK import "d" and "x" are a pair MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/45569 Reviewed-By: Antoine du Hamel Reviewed-By: Tobias Nießen --- lib/internal/crypto/cfrg.js | 22 ++++++++++++++----- test/wpt/status/WebCryptoAPI.json | 36 ------------------------------- 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/lib/internal/crypto/cfrg.js b/lib/internal/crypto/cfrg.js index 9ce38c538e2d49..4e22efa4daa7d4 100644 --- a/lib/internal/crypto/cfrg.js +++ b/lib/internal/crypto/cfrg.js @@ -300,12 +300,24 @@ async function cfrgImportKey( name, isPublic ? 'public' : 'private', usagesSet); - keyObject = createCFRGRawKey( + + const publicKeyObject = createCFRGRawKey( name, - Buffer.from( - isPublic ? keyData.x : keyData.d, - 'base64'), - isPublic); + Buffer.from(keyData.x, 'base64'), + true); + + if (isPublic) { + keyObject = publicKeyObject; + } else { + keyObject = createCFRGRawKey( + name, + Buffer.from(keyData.d, 'base64'), + false); + + if (!createPublicKey(keyObject).equals(publicKeyObject)) { + throw lazyDOMException('Invalid JWK keyData', 'DataError'); + } + } break; } case 'raw': { diff --git a/test/wpt/status/WebCryptoAPI.json b/test/wpt/status/WebCryptoAPI.json index adaa217cf202b1..9f101f6cdd92c9 100644 --- a/test/wpt/status/WebCryptoAPI.json +++ b/test/wpt/status/WebCryptoAPI.json @@ -7,41 +7,5 @@ }, "idlharness.https.any.js": { "skip": "Various non-IDL-compliant things" - }, - "import_export/okp_importKey_failures_Ed25519.https.any.js": { - "fail": { - "expected": [ - "Invalid key pair: importKey(jwk(private), {name: Ed25519}, true, [sign])", - "Invalid key pair: importKey(jwk(private), {name: Ed25519}, true, [sign, sign])" - ] - } - }, - "import_export/okp_importKey_failures_Ed448.https.any.js": { - "fail": { - "expected": [ - "Invalid key pair: importKey(jwk(private), {name: Ed448}, true, [sign])", - "Invalid key pair: importKey(jwk(private), {name: Ed448}, true, [sign, sign])" - ] - } - }, - "import_export/okp_importKey_failures_X25519.https.any.js": { - "fail": { - "expected": [ - "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveKey])", - "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveBits, deriveKey])", - "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveBits])", - "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])" - ] - } - }, - "import_export/okp_importKey_failures_X448.https.any.js": { - "fail": { - "expected": [ - "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey])", - "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", - "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits])", - "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])" - ] - } } } From 7cc99987375f96818779b808caaf4981dd2c68ff Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Tue, 22 Nov 2022 00:22:08 +0100 Subject: [PATCH 12/97] crypto: fix ECDH webcrypto public CryptoKey usages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/45569 Reviewed-By: Antoine du Hamel Reviewed-By: Tobias Nießen --- lib/internal/crypto/ec.js | 9 ++++++++- test/parallel/test-webcrypto-derivebits-ecdh.js | 12 ++++++------ test/parallel/test-webcrypto-derivekey-ecdh.js | 8 ++++---- test/parallel/test-webcrypto-export-import-ec.js | 12 ++++++------ 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/lib/internal/crypto/ec.js b/lib/internal/crypto/ec.js index 4359294d343be0..bdc59e7737828a 100644 --- a/lib/internal/crypto/ec.js +++ b/lib/internal/crypto/ec.js @@ -58,7 +58,14 @@ function verifyAcceptableEcKeyUse(name, type, usages) { let checkSet; switch (name) { case 'ECDH': - checkSet = ['deriveKey', 'deriveBits']; + switch (type) { + case 'private': + checkSet = ['deriveKey', 'deriveBits']; + break; + case 'public': + checkSet = []; + break; + } break; case 'ECDSA': switch (type) { diff --git a/test/parallel/test-webcrypto-derivebits-ecdh.js b/test/parallel/test-webcrypto-derivebits-ecdh.js index 739155fba47818..2ab152ba5a7615 100644 --- a/test/parallel/test-webcrypto-derivebits-ecdh.js +++ b/test/parallel/test-webcrypto-derivebits-ecdh.js @@ -73,7 +73,7 @@ async function prepareKeys() { namedCurve }, true, - ['deriveKey', 'deriveBits']), + []), ]); keys[namedCurve] = { privateKey, @@ -235,17 +235,17 @@ async function prepareKeys() { name: 'ECDH', public: keys['P-521'].publicKey }, keys['P-521'].publicKey, null), { - message: /baseKey must be a private key/ + name: 'InvalidAccessError' }); } { - // Base key is not a private key + // Public is not a public key await assert.rejects(subtle.deriveBits({ name: 'ECDH', public: keys['P-521'].privateKey - }, keys['P-521'].publicKey, null), { - message: /algorithm\.public must be a public key/ + }, keys['P-521'].privateKey, null), { + name: 'InvalidAccessError' }); } @@ -262,7 +262,7 @@ async function prepareKeys() { name: 'ECDH', public: key }, keys['P-521'].publicKey, null), { - message: /algorithm\.public must be a public key/ + name: 'InvalidAccessError' }); } })().then(common.mustCall()); diff --git a/test/parallel/test-webcrypto-derivekey-ecdh.js b/test/parallel/test-webcrypto-derivekey-ecdh.js index 2ca54957a55304..d3ba660fde6d5b 100644 --- a/test/parallel/test-webcrypto-derivekey-ecdh.js +++ b/test/parallel/test-webcrypto-derivekey-ecdh.js @@ -68,7 +68,7 @@ async function prepareKeys() { namedCurve }, true, - ['deriveKey', 'deriveBits']), + []), ]); keys[namedCurve] = { privateKey, @@ -209,7 +209,7 @@ async function prepareKeys() { }, keys['P-521'].publicKey, ...otherArgs), - { message: /baseKey must be a private key/ }); + { name: 'InvalidAccessError' }); } { @@ -222,7 +222,7 @@ async function prepareKeys() { }, keys['P-521'].publicKey, ...otherArgs), - { message: /algorithm\.public must be a public key/ }); + { name: 'InvalidAccessError' }); } { @@ -242,6 +242,6 @@ async function prepareKeys() { }, keys['P-521'].publicKey, ...otherArgs), - { message: /algorithm\.public must be a public key/ }); + { name: 'InvalidAccessError' }); } })().then(common.mustCall()); diff --git a/test/parallel/test-webcrypto-export-import-ec.js b/test/parallel/test-webcrypto-export-import-ec.js index 79f82a3d4adc26..ec5615b6582f88 100644 --- a/test/parallel/test-webcrypto-export-import-ec.js +++ b/test/parallel/test-webcrypto-export-import-ec.js @@ -333,19 +333,19 @@ async function testImportRaw({ name, publicUsages }, namedCurve) { const rsaPrivate = crypto.createPrivateKey( fixtures.readKey('rsa_private_2048.pem')); - for (const [name, [publicUsage, privateUsage]] of Object.entries({ - 'ECDSA': ['verify', 'sign'], - 'ECDH': ['deriveBits', 'deriveBits'], - })) { + for (const [name, publicUsages, privateUsages] of [ + ['ECDSA', ['verify'], ['sign']], + ['ECDH', [], ['deriveBits', 'deriveBits']], + ]) { assert.rejects(subtle.importKey( 'spki', rsaPublic.export({ format: 'der', type: 'spki' }), { name, hash: 'SHA-256', namedCurve: 'P-256' }, - true, [publicUsage]), { message: /Invalid key type/ }); + true, publicUsages), { message: /Invalid key type/ }); assert.rejects(subtle.importKey( 'pkcs8', rsaPrivate.export({ format: 'der', type: 'pkcs8' }), { name, hash: 'SHA-256', namedCurve: 'P-256' }, - true, [privateUsage]), { message: /Invalid key type/ }); + true, privateUsages), { message: /Invalid key type/ }); } } From 845f805490b353d07e8ca7da5781828495b25394 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Tue, 22 Nov 2022 12:46:05 +0100 Subject: [PATCH 13/97] crypto: refactor verify acceptable key usage functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/45569 Reviewed-By: Antoine du Hamel Reviewed-By: Tobias Nießen --- lib/internal/crypto/cfrg.js | 32 +++++++++++------------------- lib/internal/crypto/ec.js | 39 +++++++++++++------------------------ lib/internal/crypto/rsa.js | 32 ++++++++++++------------------ 3 files changed, 37 insertions(+), 66 deletions(-) diff --git a/lib/internal/crypto/cfrg.js b/lib/internal/crypto/cfrg.js index 4e22efa4daa7d4..6f098df41530b2 100644 --- a/lib/internal/crypto/cfrg.js +++ b/lib/internal/crypto/cfrg.js @@ -47,32 +47,22 @@ const { const generateKeyPair = promisify(_generateKeyPair); -function verifyAcceptableCfrgKeyUse(name, type, usages) { +function verifyAcceptableCfrgKeyUse(name, isPublic, usages) { let checkSet; switch (name) { case 'X25519': // Fall through case 'X448': - switch (type) { - case 'private': - checkSet = ['deriveKey', 'deriveBits']; - break; - case 'public': - checkSet = []; - break; - } + checkSet = isPublic ? [] : ['deriveKey', 'deriveBits']; break; case 'Ed25519': // Fall through case 'Ed448': - switch (type) { - case 'private': - checkSet = ['sign']; - break; - case 'public': - checkSet = ['verify']; - break; - } + checkSet = isPublic ? ['verify'] : ['sign']; + break; + default: + throw lazyDOMException( + 'The algorithm is not supported', 'NotSupportedError'); } if (hasAnyNotIn(usages, checkSet)) { throw lazyDOMException( @@ -219,7 +209,7 @@ async function cfrgImportKey( const usagesSet = new SafeSet(keyUsages); switch (format) { case 'spki': { - verifyAcceptableCfrgKeyUse(name, 'public', usagesSet); + verifyAcceptableCfrgKeyUse(name, true, usagesSet); try { keyObject = createPublicKey({ key: keyData, @@ -233,7 +223,7 @@ async function cfrgImportKey( break; } case 'pkcs8': { - verifyAcceptableCfrgKeyUse(name, 'private', usagesSet); + verifyAcceptableCfrgKeyUse(name, false, usagesSet); try { keyObject = createPrivateKey({ key: keyData, @@ -298,7 +288,7 @@ async function cfrgImportKey( verifyAcceptableCfrgKeyUse( name, - isPublic ? 'public' : 'private', + isPublic, usagesSet); const publicKeyObject = createCFRGRawKey( @@ -321,7 +311,7 @@ async function cfrgImportKey( break; } case 'raw': { - verifyAcceptableCfrgKeyUse(name, 'public', usagesSet); + verifyAcceptableCfrgKeyUse(name, true, usagesSet); keyObject = createCFRGRawKey(name, keyData, true); break; } diff --git a/lib/internal/crypto/ec.js b/lib/internal/crypto/ec.js index bdc59e7737828a..ef9e4f26b7759f 100644 --- a/lib/internal/crypto/ec.js +++ b/lib/internal/crypto/ec.js @@ -54,28 +54,18 @@ const { const generateKeyPair = promisify(_generateKeyPair); -function verifyAcceptableEcKeyUse(name, type, usages) { +function verifyAcceptableEcKeyUse(name, isPublic, usages) { let checkSet; switch (name) { case 'ECDH': - switch (type) { - case 'private': - checkSet = ['deriveKey', 'deriveBits']; - break; - case 'public': - checkSet = []; - break; - } + checkSet = isPublic ? [] : ['deriveKey', 'deriveBits']; break; case 'ECDSA': - switch (type) { - case 'private': - checkSet = ['sign']; - break; - case 'public': - checkSet = ['verify']; - break; - } + checkSet = isPublic ? ['verify'] : ['sign']; + break; + default: + throw lazyDOMException( + 'The algorithm is not supported', 'NotSupportedError'); } if (hasAnyNotIn(usages, checkSet)) { throw lazyDOMException( @@ -186,7 +176,7 @@ async function ecImportKey( const usagesSet = new SafeSet(keyUsages); switch (format) { case 'spki': { - verifyAcceptableEcKeyUse(name, 'public', usagesSet); + verifyAcceptableEcKeyUse(name, true, usagesSet); try { keyObject = createPublicKey({ key: keyData, @@ -200,7 +190,7 @@ async function ecImportKey( break; } case 'pkcs8': { - verifyAcceptableEcKeyUse(name, 'private', usagesSet); + verifyAcceptableEcKeyUse(name, false, usagesSet); try { keyObject = createPrivateKey({ key: keyData, @@ -221,11 +211,10 @@ async function ecImportKey( if (keyData.crv !== namedCurve) throw lazyDOMException('Named curve mismatch', 'DataError'); - if (keyData.d !== undefined) { - verifyAcceptableEcKeyUse(name, 'private', usagesSet); - } else { - verifyAcceptableEcKeyUse(name, 'public', usagesSet); - } + verifyAcceptableEcKeyUse( + name, + keyData.d === undefined, + usagesSet); if (usagesSet.size > 0 && keyData.use !== undefined) { if (algorithm.name === 'ECDSA' && keyData.use !== 'sig') @@ -265,7 +254,7 @@ async function ecImportKey( break; } case 'raw': { - verifyAcceptableEcKeyUse(name, 'public', usagesSet); + verifyAcceptableEcKeyUse(name, true, usagesSet); keyObject = createECPublicKeyRaw(namedCurve, keyData); break; } diff --git a/lib/internal/crypto/rsa.js b/lib/internal/crypto/rsa.js index 0ec169a116295e..5776d06fd18699 100644 --- a/lib/internal/crypto/rsa.js +++ b/lib/internal/crypto/rsa.js @@ -74,28 +74,20 @@ const kRsaVariants = { }; const generateKeyPair = promisify(_generateKeyPair); -function verifyAcceptableRsaKeyUse(name, type, usages) { +function verifyAcceptableRsaKeyUse(name, isPublic, usages) { let checkSet; switch (name) { case 'RSA-OAEP': - switch (type) { - case 'private': - checkSet = ['decrypt', 'unwrapKey']; - break; - case 'public': - checkSet = ['encrypt', 'wrapKey']; - break; - } + checkSet = isPublic ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey']; + break; + case 'RSA-PSS': + // Fall through + case 'RSASSA-PKCS1-v1_5': + checkSet = isPublic ? ['verify'] : ['sign']; break; default: - switch (type) { - case 'private': - checkSet = ['sign']; - break; - case 'public': - checkSet = ['verify']; - break; - } + throw lazyDOMException( + 'The algorithm is not supported', 'NotSupportedError'); } if (hasAnyNotIn(usages, checkSet)) { throw lazyDOMException( @@ -244,7 +236,7 @@ async function rsaImportKey( let keyObject; switch (format) { case 'spki': { - verifyAcceptableRsaKeyUse(algorithm.name, 'public', usagesSet); + verifyAcceptableRsaKeyUse(algorithm.name, true, usagesSet); try { keyObject = createPublicKey({ key: keyData, @@ -258,7 +250,7 @@ async function rsaImportKey( break; } case 'pkcs8': { - verifyAcceptableRsaKeyUse(algorithm.name, 'private', usagesSet); + verifyAcceptableRsaKeyUse(algorithm.name, false, usagesSet); try { keyObject = createPrivateKey({ key: keyData, @@ -277,7 +269,7 @@ async function rsaImportKey( verifyAcceptableRsaKeyUse( algorithm.name, - keyData.d !== undefined ? 'private' : 'public', + keyData.d === undefined, usagesSet); if (keyData.kty !== 'RSA') From aabfdef86141cb191495af1adc595fea6bacc6cc Mon Sep 17 00:00:00 2001 From: Deokjin Kim Date: Sat, 26 Nov 2022 23:48:32 +0900 Subject: [PATCH 14/97] doc: use console.error for error case in fs, https, net and process console.error is more suitable than console.log for error case. PR-URL: https://github.com/nodejs/node/pull/45606 Reviewed-By: Ruben Bridgewater Reviewed-By: Antoine du Hamel Reviewed-By: Matteo Collina Reviewed-By: Luigi Pinca Reviewed-By: Kohei Ueno Reviewed-By: Colin Ihrig Reviewed-By: Paolo Insogna --- doc/api/fs.md | 4 ++-- doc/api/https.md | 4 ++-- doc/api/net.md | 2 +- doc/api/process.md | 20 ++++++++++---------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index 3fd65cc8d9cf13..4d7362a7f2c98f 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -941,7 +941,7 @@ try { await copyFile('source.txt', 'destination.txt'); console.log('source.txt was copied to destination.txt'); } catch { - console.log('The file could not be copied'); + console.error('The file could not be copied'); } // By using COPYFILE_EXCL, the operation will fail if destination.txt exists. @@ -949,7 +949,7 @@ try { await copyFile('source.txt', 'destination.txt', constants.COPYFILE_EXCL); console.log('source.txt was copied to destination.txt'); } catch { - console.log('The file could not be copied'); + console.error('The file could not be copied'); } ``` diff --git a/doc/api/https.md b/doc/api/https.md index eb5636168b54dd..71bd6cb642ed4f 100644 --- a/doc/api/https.md +++ b/doc/api/https.md @@ -24,7 +24,7 @@ let https; try { https = require('node:https'); } catch (err) { - console.log('https support is disabled!'); + console.error('https support is disabled!'); } ``` @@ -42,7 +42,7 @@ let https; try { https = await import('node:https'); } catch (err) { - console.log('https support is disabled!'); + console.error('https support is disabled!'); } ``` diff --git a/doc/api/net.md b/doc/api/net.md index a72d3bb3a66c8a..e6555e5415470f 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -409,7 +409,7 @@ after a certain amount of time: ```js server.on('error', (e) => { if (e.code === 'EADDRINUSE') { - console.log('Address in use, retrying...'); + console.error('Address in use, retrying...'); setTimeout(() => { server.close(); server.listen(PORT, HOST); diff --git a/doc/api/process.md b/doc/api/process.md index 31210abf13824e..262d7a496fc41b 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -3186,7 +3186,7 @@ if (process.getegid && process.setegid) { process.setegid(501); console.log(`New gid: ${process.getegid()}`); } catch (err) { - console.log(`Failed to set gid: ${err}`); + console.error(`Failed to set gid: ${err}`); } } ``` @@ -3200,7 +3200,7 @@ if (process.getegid && process.setegid) { process.setegid(501); console.log(`New gid: ${process.getegid()}`); } catch (err) { - console.log(`Failed to set gid: ${err}`); + console.error(`Failed to set gid: ${err}`); } } ``` @@ -3231,7 +3231,7 @@ if (process.geteuid && process.seteuid) { process.seteuid(501); console.log(`New uid: ${process.geteuid()}`); } catch (err) { - console.log(`Failed to set uid: ${err}`); + console.error(`Failed to set uid: ${err}`); } } ``` @@ -3245,7 +3245,7 @@ if (process.geteuid && process.seteuid) { process.seteuid(501); console.log(`New uid: ${process.geteuid()}`); } catch (err) { - console.log(`Failed to set uid: ${err}`); + console.error(`Failed to set uid: ${err}`); } } ``` @@ -3276,7 +3276,7 @@ if (process.getgid && process.setgid) { process.setgid(501); console.log(`New gid: ${process.getgid()}`); } catch (err) { - console.log(`Failed to set gid: ${err}`); + console.error(`Failed to set gid: ${err}`); } } ``` @@ -3290,7 +3290,7 @@ if (process.getgid && process.setgid) { process.setgid(501); console.log(`New gid: ${process.getgid()}`); } catch (err) { - console.log(`Failed to set gid: ${err}`); + console.error(`Failed to set gid: ${err}`); } } ``` @@ -3321,7 +3321,7 @@ if (process.getgroups && process.setgroups) { process.setgroups([501]); console.log(process.getgroups()); // new groups } catch (err) { - console.log(`Failed to set groups: ${err}`); + console.error(`Failed to set groups: ${err}`); } } ``` @@ -3334,7 +3334,7 @@ if (process.getgroups && process.setgroups) { process.setgroups([501]); console.log(process.getgroups()); // new groups } catch (err) { - console.log(`Failed to set groups: ${err}`); + console.error(`Failed to set groups: ${err}`); } } ``` @@ -3365,7 +3365,7 @@ if (process.getuid && process.setuid) { process.setuid(501); console.log(`New uid: ${process.getuid()}`); } catch (err) { - console.log(`Failed to set uid: ${err}`); + console.error(`Failed to set uid: ${err}`); } } ``` @@ -3379,7 +3379,7 @@ if (process.getuid && process.setuid) { process.setuid(501); console.log(`New uid: ${process.getuid()}`); } catch (err) { - console.log(`Failed to set uid: ${err}`); + console.error(`Failed to set uid: ${err}`); } } ``` From 56eee72abbd607447d4ee34421bf43aca3c8c6e1 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Sat, 26 Nov 2022 23:47:41 -0500 Subject: [PATCH 15/97] stream: use structuredClone instead of v8 PR-URL: https://github.com/nodejs/node/pull/45611 Reviewed-By: Erick Wendel Reviewed-By: Antoine du Hamel Reviewed-By: Luigi Pinca --- lib/internal/webstreams/readablestream.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/internal/webstreams/readablestream.js b/lib/internal/webstreams/readablestream.js index 256075d3a1cfaa..f53fb53a7e3825 100644 --- a/lib/internal/webstreams/readablestream.js +++ b/lib/internal/webstreams/readablestream.js @@ -55,11 +55,6 @@ const { kEnumerableProperty, } = require('internal/util'); -const { - serialize, - deserialize, -} = require('v8'); - const { validateBuffer, validateObject, @@ -90,6 +85,10 @@ const { kIsReadable, } = require('internal/streams/utils'); +const { + structuredClone, +} = require('internal/structured_clone'); + const { ArrayBufferViewGetBuffer, ArrayBufferViewGetByteLength, @@ -1470,8 +1469,7 @@ function readableStreamDefaultTee(stream, cloneForBranch2) { const value1 = value; let value2 = value; if (!canceled2 && cloneForBranch2) { - // Structured Clone - value2 = deserialize(serialize(value2)); + value2 = structuredClone(value2); } if (!canceled1) { readableStreamDefaultControllerEnqueue( From 049ef342c6c7712632df99ec718192cc79430f51 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Sat, 26 Nov 2022 23:47:49 -0500 Subject: [PATCH 16/97] meta: update AUTHORS PR-URL: https://github.com/nodejs/node/pull/45637 Reviewed-By: Yagiz Nizipli Reviewed-By: Rich Trott Reviewed-By: Moshe Atlow --- AUTHORS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/AUTHORS b/AUTHORS index 38ea282c199fc2..40a198d82e031b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3575,5 +3575,11 @@ Takuro Sato <79583855+takuro-sato@users.noreply.github.com> Carter Snook Nathanael Ruf <104262550+nathanael-ruf@users.noreply.github.com> Vasili Skurydzin +翠 / green +Ulises Gascon +chlorine +Shi Lei +Deokjin Kim +Marco Ippolito # Generated by tools/update-authors.mjs From 17847683dcdd62c425acf940ca4a8d2b02c3ba42 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Sun, 27 Nov 2022 01:48:51 -0500 Subject: [PATCH 17/97] buffer: make decodeUTF8 params loose PR-URL: https://github.com/nodejs/node/pull/45610 Reviewed-By: Anna Henningsen Reviewed-By: Darshan Sen --- src/node_buffer.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node_buffer.cc b/src/node_buffer.cc index acec3c420ce1d2..3a9aeb99a89240 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -570,6 +570,8 @@ void StringSlice(const FunctionCallbackInfo& args) { void DecodeUTF8(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); // list, flags + CHECK_GE(args.Length(), 1); + if (!(args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer() || args[0]->IsArrayBufferView())) { return node::THROW_ERR_INVALID_ARG_TYPE( @@ -580,7 +582,6 @@ void DecodeUTF8(const FunctionCallbackInfo& args) { ArrayBufferViewContents buffer(args[0]); - CHECK(args[1]->IsBoolean()); bool ignore_bom = args[1]->IsTrue(); const char* data = buffer.data(); From d272faa54dba66b456c089e09454e13064f899a2 Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Fri, 18 Nov 2022 00:26:18 +0200 Subject: [PATCH 18/97] fs: fix `nonNativeWatcher` leak of `StatWatchers` PR-URL: https://github.com/nodejs/node/pull/45501 Reviewed-By: Yagiz Nizipli --- lib/internal/fs/recursive_watch.js | 9 +++----- test/parallel/test-fs-watch-recursive.js | 27 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/internal/fs/recursive_watch.js b/lib/internal/fs/recursive_watch.js index ed146fa28bed8d..5c7a3e5b39a9a2 100644 --- a/lib/internal/fs/recursive_watch.js +++ b/lib/internal/fs/recursive_watch.js @@ -154,14 +154,11 @@ class FSWatcher extends EventEmitter { this.#symbolicFiles.add(f); } + this.#files.set(f, file); if (file.isFile()) { this.#watchFile(f); - } else { - this.#files.set(f, file); - - if (file.isDirectory() && !file.isSymbolicLink()) { - await this.#watchFolder(f); - } + } else if (file.isDirectory() && !file.isSymbolicLink()) { + await this.#watchFolder(f); } } } diff --git a/test/parallel/test-fs-watch-recursive.js b/test/parallel/test-fs-watch-recursive.js index a4c22bfbd2ce11..a7ba72dab4dcb9 100644 --- a/test/parallel/test-fs-watch-recursive.js +++ b/test/parallel/test-fs-watch-recursive.js @@ -213,6 +213,33 @@ tmpdir.refresh(); }); })().then(common.mustCall()); + +(async () => { + // Assert recursive watch does not leak handles + const rootDirectory = fs.mkdtempSync(testDir + path.sep); + const testDirectory = path.join(rootDirectory, 'test-7'); + const filePath = path.join(testDirectory, 'only-file.txt'); + fs.mkdirSync(testDirectory); + + let watcherClosed = false; + const watcher = fs.watch(testDirectory, { recursive: true }); + watcher.on('change', common.mustCallAtLeast(async (event, filename) => { + await setTimeout(common.platformTimeout(100)); + if (filename === path.basename(filePath)) { + watcher.close(); + watcherClosed = true; + } + await setTimeout(common.platformTimeout(100)); + assert(!process._getActiveHandles().some((handle) => handle.constructor.name === 'StatWatcher')); + })); + + process.on('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + }); + await setTimeout(common.platformTimeout(100)); + fs.writeFileSync(filePath, 'content'); +})().then(common.mustCall()); + (async () => { // Handle non-boolean values for options.recursive From fbd2d27789eac8271285a8296eb7d06d24126f17 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Sun, 27 Nov 2022 04:45:27 -0500 Subject: [PATCH 19/97] deps: update corepack to 0.15.2 PR-URL: https://github.com/nodejs/node/pull/45635 Reviewed-By: Moshe Atlow Reviewed-By: Antoine du Hamel --- deps/corepack/CHANGELOG.md | 7 +++++++ deps/corepack/dist/corepack.js | 4 ++-- deps/corepack/package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/deps/corepack/CHANGELOG.md b/deps/corepack/CHANGELOG.md index b66e53f806d035..30f1a9db99c864 100644 --- a/deps/corepack/CHANGELOG.md +++ b/deps/corepack/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.15.2](https://github.com/nodejs/corepack/compare/v0.15.1...v0.15.2) (2022-11-25) + + +### Features + +* update package manager versions ([#211](https://github.com/nodejs/corepack/issues/211)) ([c536c0c](https://github.com/nodejs/corepack/commit/c536c0c27c137c87a14487a2c2a63a1fe6bf88ec)) + ## [0.15.1](https://github.com/nodejs/corepack/compare/v0.15.0...v0.15.1) (2022-11-04) diff --git a/deps/corepack/dist/corepack.js b/deps/corepack/dist/corepack.js index 122b79d491c37f..4e48cd7a82f6a8 100755 --- a/deps/corepack/dist/corepack.js +++ b/deps/corepack/dist/corepack.js @@ -17099,7 +17099,7 @@ const supportsColor = { /***/ ((module) => { "use strict"; -module.exports = JSON.parse('{"definitions":{"npm":{"default":"8.19.3+sha1.adb51bf8886d519dd4df162726d0ad157ecfa272","fetchLatestFrom":{"type":"npm","package":"npm"},"transparent":{"commands":[["npm","init"],["npx"]]},"ranges":{"*":{"url":"https://registry.npmjs.org/npm/-/npm-{}.tgz","bin":{"npm":"./bin/npm-cli.js","npx":"./bin/npx-cli.js"},"registry":{"type":"npm","package":"npm"}}}},"pnpm":{"default":"7.14.2+sha1.73bf8dbd968bf782db5bbc627d2facc645c6bc4a","fetchLatestFrom":{"type":"npm","package":"pnpm"},"transparent":{"commands":[["pnpm","init"],["pnpx"],["pnpm","dlx"]]},"ranges":{"<6.0.0":{"url":"https://registry.npmjs.org/pnpm/-/pnpm-{}.tgz","bin":{"pnpm":"./bin/pnpm.js","pnpx":"./bin/pnpx.js"},"registry":{"type":"npm","package":"pnpm"}},">=6.0.0":{"url":"https://registry.npmjs.org/pnpm/-/pnpm-{}.tgz","bin":{"pnpm":"./bin/pnpm.cjs","pnpx":"./bin/pnpx.cjs"},"registry":{"type":"npm","package":"pnpm"}}}},"yarn":{"default":"1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447","fetchLatestFrom":{"type":"npm","package":"yarn"},"transparent":{"default":"3.2.4+sha224.e61785c1cff5bae29570238b7718845a9d00788f5b6f32e472c908dc","commands":[["yarn","dlx"]]},"ranges":{"<2.0.0":{"url":"https://registry.yarnpkg.com/yarn/-/yarn-{}.tgz","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"registry":{"type":"npm","package":"yarn"}},">=2.0.0":{"name":"yarn","url":"https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js","bin":["yarn","yarnpkg"],"registry":{"type":"url","url":"https://repo.yarnpkg.com/tags","fields":{"tags":"latest","versions":"tags"}}}}}}}'); +module.exports = JSON.parse('{"definitions":{"npm":{"default":"9.1.2+sha1.0cf57d747a84fcc32ed397545f5bea6dbb014141","fetchLatestFrom":{"type":"npm","package":"npm"},"transparent":{"commands":[["npm","init"],["npx"]]},"ranges":{"*":{"url":"https://registry.npmjs.org/npm/-/npm-{}.tgz","bin":{"npm":"./bin/npm-cli.js","npx":"./bin/npx-cli.js"},"registry":{"type":"npm","package":"npm"}}}},"pnpm":{"default":"7.17.0+sha1.3470fe6fbeee107f01cb1878a27c931099c36e3a","fetchLatestFrom":{"type":"npm","package":"pnpm"},"transparent":{"commands":[["pnpm","init"],["pnpx"],["pnpm","dlx"]]},"ranges":{"<6.0.0":{"url":"https://registry.npmjs.org/pnpm/-/pnpm-{}.tgz","bin":{"pnpm":"./bin/pnpm.js","pnpx":"./bin/pnpx.js"},"registry":{"type":"npm","package":"pnpm"}},">=6.0.0":{"url":"https://registry.npmjs.org/pnpm/-/pnpm-{}.tgz","bin":{"pnpm":"./bin/pnpm.cjs","pnpx":"./bin/pnpx.cjs"},"registry":{"type":"npm","package":"pnpm"}}}},"yarn":{"default":"1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447","fetchLatestFrom":{"type":"npm","package":"yarn"},"transparent":{"default":"3.3.0+sha224.c2301c8aea8dc8a09277d8e580d29ec51bd2d4aaec28617a8c65f870","commands":[["yarn","dlx"]]},"ranges":{"<2.0.0":{"url":"https://registry.yarnpkg.com/yarn/-/yarn-{}.tgz","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"registry":{"type":"npm","package":"yarn"}},">=2.0.0":{"name":"yarn","url":"https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js","bin":["yarn","yarnpkg"],"registry":{"type":"url","url":"https://repo.yarnpkg.com/tags","fields":{"tags":"latest","versions":"tags"}}}}}}}'); /***/ }), @@ -17110,7 +17110,7 @@ module.exports = JSON.parse('{"definitions":{"npm":{"default":"8.19.3+sha1.adb51 /***/ ((module) => { "use strict"; -module.exports = JSON.parse('{"name":"corepack","version":"0.15.1","homepage":"https://github.com/nodejs/corepack#readme","bugs":{"url":"https://github.com/nodejs/corepack/issues"},"repository":{"type":"git","url":"https://github.com/nodejs/corepack.git"},"license":"MIT","packageManager":"yarn@4.0.0-rc.15+sha224.7fa5c1d1875b041cea8fcbf9a364667e398825364bf5c5c8cd5f6601","devDependencies":{"@babel/core":"^7.14.3","@babel/plugin-transform-modules-commonjs":"^7.14.0","@babel/preset-typescript":"^7.13.0","@types/debug":"^4.1.5","@types/jest":"^29.0.0","@types/node":"^18.0.0","@types/semver":"^7.1.0","@types/tar":"^6.0.0","@types/which":"^2.0.0","@typescript-eslint/eslint-plugin":"^5.0.0","@typescript-eslint/parser":"^5.0.0","@yarnpkg/eslint-config":"^1.0.0-rc.5","@yarnpkg/fslib":"^2.1.0","@zkochan/cmd-shim":"^5.0.0","babel-plugin-dynamic-import-node":"^2.3.3","clipanion":"^3.0.1","debug":"^4.1.1","eslint":"^8.0.0","eslint-plugin-arca":"^0.15.0","jest":"^29.0.0","nock":"^13.0.4","proxy-agent":"^5.0.0","semver":"^7.1.3","supports-color":"^9.0.0","tar":"^6.0.1","terser-webpack-plugin":"^5.1.2","ts-loader":"^9.0.0","ts-node":"^10.0.0","typescript":"^4.3.2","v8-compile-cache":"^2.3.0","webpack":"^5.38.1","webpack-cli":"^4.0.0","which":"^2.0.2"},"scripts":{"build":"rm -rf dist shims && webpack && ts-node ./mkshims.ts","corepack":"ts-node ./sources/_entryPoint.ts","lint":"yarn eslint","prepack":"yarn build","postpack":"rm -rf dist shims","typecheck":"tsc --noEmit","test":"yarn jest"},"files":["dist","shims","LICENSE.md"],"publishConfig":{"bin":{"corepack":"./dist/corepack.js","pnpm":"./dist/pnpm.js","pnpx":"./dist/pnpx.js","yarn":"./dist/yarn.js","yarnpkg":"./dist/yarnpkg.js"},"executableFiles":["./dist/npm.js","./dist/npx.js","./dist/pnpm.js","./dist/pnpx.js","./dist/yarn.js","./dist/yarnpkg.js","./dist/corepack.js","./shims/npm","./shims/npm.ps1","./shims/npx","./shims/npx.ps1","./shims/pnpm","./shims/pnpm.ps1","./shims/pnpx","./shims/pnpx.ps1","./shims/yarn","./shims/yarn.ps1","./shims/yarnpkg","./shims/yarnpkg.ps1"]},"resolutions":{"vm2":"patch:vm2@npm:3.9.9#.yarn/patches/vm2-npm-3.9.9-03fd1f4dc5.patch"}}'); +module.exports = JSON.parse('{"name":"corepack","version":"0.15.2","homepage":"https://github.com/nodejs/corepack#readme","bugs":{"url":"https://github.com/nodejs/corepack/issues"},"repository":{"type":"git","url":"https://github.com/nodejs/corepack.git"},"license":"MIT","packageManager":"yarn@4.0.0-rc.15+sha224.7fa5c1d1875b041cea8fcbf9a364667e398825364bf5c5c8cd5f6601","devDependencies":{"@babel/core":"^7.14.3","@babel/plugin-transform-modules-commonjs":"^7.14.0","@babel/preset-typescript":"^7.13.0","@types/debug":"^4.1.5","@types/jest":"^29.0.0","@types/node":"^18.0.0","@types/semver":"^7.1.0","@types/tar":"^6.0.0","@types/which":"^2.0.0","@typescript-eslint/eslint-plugin":"^5.0.0","@typescript-eslint/parser":"^5.0.0","@yarnpkg/eslint-config":"^1.0.0-rc.5","@yarnpkg/fslib":"^2.1.0","@zkochan/cmd-shim":"^5.0.0","babel-plugin-dynamic-import-node":"^2.3.3","clipanion":"^3.0.1","debug":"^4.1.1","eslint":"^8.0.0","eslint-plugin-arca":"^0.15.0","jest":"^29.0.0","nock":"^13.0.4","proxy-agent":"^5.0.0","semver":"^7.1.3","supports-color":"^9.0.0","tar":"^6.0.1","terser-webpack-plugin":"^5.1.2","ts-loader":"^9.0.0","ts-node":"^10.0.0","typescript":"^4.3.2","v8-compile-cache":"^2.3.0","webpack":"^5.38.1","webpack-cli":"^4.0.0","which":"^2.0.2"},"scripts":{"build":"rm -rf dist shims && webpack && ts-node ./mkshims.ts","corepack":"ts-node ./sources/_entryPoint.ts","lint":"yarn eslint","prepack":"yarn build","postpack":"rm -rf dist shims","typecheck":"tsc --noEmit","test":"yarn jest"},"files":["dist","shims","LICENSE.md"],"publishConfig":{"bin":{"corepack":"./dist/corepack.js","pnpm":"./dist/pnpm.js","pnpx":"./dist/pnpx.js","yarn":"./dist/yarn.js","yarnpkg":"./dist/yarnpkg.js"},"executableFiles":["./dist/npm.js","./dist/npx.js","./dist/pnpm.js","./dist/pnpx.js","./dist/yarn.js","./dist/yarnpkg.js","./dist/corepack.js","./shims/npm","./shims/npm.ps1","./shims/npx","./shims/npx.ps1","./shims/pnpm","./shims/pnpm.ps1","./shims/pnpx","./shims/pnpx.ps1","./shims/yarn","./shims/yarn.ps1","./shims/yarnpkg","./shims/yarnpkg.ps1"]},"resolutions":{"vm2":"patch:vm2@npm:3.9.9#.yarn/patches/vm2-npm-3.9.9-03fd1f4dc5.patch"}}'); /***/ }) diff --git a/deps/corepack/package.json b/deps/corepack/package.json index 774c5f88d4d845..616a7a42c16a27 100644 --- a/deps/corepack/package.json +++ b/deps/corepack/package.json @@ -1,6 +1,6 @@ { "name": "corepack", - "version": "0.15.1", + "version": "0.15.2", "homepage": "https://github.com/nodejs/corepack#readme", "bugs": { "url": "https://github.com/nodejs/corepack/issues" From 573eab9235fa85100beef5de6d2ffcbd3322ad1e Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 27 Nov 2022 19:27:21 +0100 Subject: [PATCH 20/97] crypto: refactor ArrayBuffer to bigint conversion utils PR-URL: https://github.com/nodejs/node/pull/45567 Refs: https://github.com/nodejs/performance/issues/16 Reviewed-By: Yagiz Nizipli Reviewed-By: James M Snell --- lib/internal/crypto/random.js | 35 ++++++++++++++++++++++--- lib/internal/per_context/primordials.js | 2 ++ typings/primordials.d.ts | 2 ++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/internal/crypto/random.js b/lib/internal/crypto/random.js index 6705bcd2e7d592..fe76cf1a69ef43 100644 --- a/lib/internal/crypto/random.js +++ b/lib/internal/crypto/random.js @@ -2,17 +2,22 @@ const { Array, + ArrayBufferPrototypeGetByteLength, ArrayPrototypeForEach, ArrayPrototypePush, ArrayPrototypeShift, ArrayPrototypeSplice, BigInt, + BigIntPrototypeToString, + DataView, + DataViewPrototypeGetUint8, FunctionPrototypeBind, FunctionPrototypeCall, MathMin, NumberIsNaN, NumberIsSafeInteger, NumberPrototypeToString, + StringFromCharCodeApply, StringPrototypePadStart, } = primordials; @@ -493,8 +498,30 @@ function generatePrimeSync(size, options = kEmptyObject) { return job.result(prime); } -function arrayBufferToUnsignedBigInt(arrayBuffer) { - return BigInt(`0x${Buffer.from(arrayBuffer).toString('hex')}`); +/** + * 48 is the ASCII code for '0', 97 is the ASCII code for 'a'. + * @param {number} number An integer between 0 and 15. + * @returns {number} corresponding to the ASCII code of the hex representation + * of the parameter. + */ +const numberToHexCharCode = (number) => (number < 10 ? 48 : 87) + number; + +/** + * @param {ArrayBuffer} buf An ArrayBuffer. + * @return {bigint} + */ +function arrayBufferToUnsignedBigInt(buf) { + const length = ArrayBufferPrototypeGetByteLength(buf); + const chars = Array(length * 2); + const view = new DataView(buf); + + for (let i = 0; i < length; i++) { + const val = DataViewPrototypeGetUint8(view, i); + chars[2 * i] = numberToHexCharCode(val >> 4); + chars[2 * i + 1] = numberToHexCharCode(val & 0xf); + } + + return BigInt(`0x${StringFromCharCodeApply(chars)}`); } function unsignedBigIntToBuffer(bigint, name) { @@ -502,8 +529,8 @@ function unsignedBigIntToBuffer(bigint, name) { throw new ERR_OUT_OF_RANGE(name, '>= 0', bigint); } - const hex = bigint.toString(16); - const padded = hex.padStart(hex.length + (hex.length % 2), 0); + const hex = BigIntPrototypeToString(bigint, 16); + const padded = StringPrototypePadStart(hex, hex.length + (hex.length % 2), 0); return Buffer.from(padded, 'hex'); } diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index bc903d02849e41..370f5953dc78e5 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -45,6 +45,8 @@ const varargsMethods = [ 'MathHypot', 'MathMax', 'MathMin', + 'StringFromCharCode', + 'StringFromCodePoint', 'StringPrototypeConcat', 'TypedArrayOf', ]; diff --git a/typings/primordials.d.ts b/typings/primordials.d.ts index 72d5a36701f651..4dcbac3abc9425 100644 --- a/typings/primordials.d.ts +++ b/typings/primordials.d.ts @@ -422,7 +422,9 @@ declare namespace primordials { export const StringName: typeof String.name export const StringPrototype: typeof String.prototype export const StringFromCharCode: typeof String.fromCharCode + export const StringFromCharCodeApply: StaticApply export const StringFromCodePoint: typeof String.fromCodePoint + export const StringFromCodePointApply: StaticApply export const StringRaw: typeof String.raw export const StringPrototypeAnchor: UncurryThis export const StringPrototypeBig: UncurryThis From 2e767bf18b0e21ccacbd1f5805a4560c328f7423 Mon Sep 17 00:00:00 2001 From: Colin Ihrig Date: Sun, 27 Nov 2022 15:52:30 -0500 Subject: [PATCH 21/97] doc: move os.machine() docs to sorted position This commit moves the os.machine() docs so that the API list is sorted. PR-URL: https://github.com/nodejs/node/pull/45647 Reviewed-By: Richard Lau Reviewed-By: Moshe Atlow Reviewed-By: Luigi Pinca --- doc/api/os.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/doc/api/os.md b/doc/api/os.md index 245371e9cfac4a..e2e702dcea5ef4 100644 --- a/doc/api/os.md +++ b/doc/api/os.md @@ -220,6 +220,24 @@ system and expressed as a fractional number. The load average is a Unix-specific concept. On Windows, the return value is always `[0, 0, 0]`. +## `os.machine()` + + + +* Returns {string} + +Returns the machine type as a string, such as `arm`, `arm64`, `aarch64`, +`mips`, `mips64`, `ppc64`, `ppc64le`, `s390`, `s390x`, `i386`, `i686`, `x86_64`. + +On POSIX systems, the machine type is determined by calling +[`uname(3)`][]. On Windows, `RtlGetVersion()` is used, and if it is not +available, `GetVersionExW()` will be used. See + for more information. + ## `os.networkInterfaces()` - -* Returns {string} - -Returns the machine type as a string, such as `arm`, `arm64`, `aarch64`, -`mips`, `mips64`, `ppc64`, `ppc64le`, `s390`, `s390x`, `i386`, `i686`, `x86_64`. - -On POSIX systems, the machine type is determined by calling -[`uname(3)`][]. On Windows, `RtlGetVersion()` is used, and if it is not -available, `GetVersionExW()` will be used. See - for more information. - ## OS constants The following constants are exported by `os.constants`. From e4316124faa5276f57ed3660ee1b1c06b4830202 Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Mon, 28 Nov 2022 00:03:42 +0200 Subject: [PATCH 22/97] fs: fix `nonNativeWatcher` watching folder with existing files PR-URL: https://github.com/nodejs/node/pull/45500 Reviewed-By: Yagiz Nizipli --- lib/internal/fs/recursive_watch.js | 3 +++ test/parallel/test-fs-watch-recursive.js | 31 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/lib/internal/fs/recursive_watch.js b/lib/internal/fs/recursive_watch.js index 5c7a3e5b39a9a2..cbe513a166ec20 100644 --- a/lib/internal/fs/recursive_watch.js +++ b/lib/internal/fs/recursive_watch.js @@ -199,6 +199,9 @@ class FSWatcher extends EventEmitter { this.emit('change', 'rename', pathRelative(this.#rootPath, file)); } else if (currentStats.isDirectory()) { this.#watchFolder(file); + } else { + // Watching a directory will trigger a change event for child files) + this.emit('change', 'change', pathRelative(this.#rootPath, file)); } }); } diff --git a/test/parallel/test-fs-watch-recursive.js b/test/parallel/test-fs-watch-recursive.js index a7ba72dab4dcb9..6fd45db63824b0 100644 --- a/test/parallel/test-fs-watch-recursive.js +++ b/test/parallel/test-fs-watch-recursive.js @@ -22,6 +22,37 @@ const tmpdir = require('../common/tmpdir'); const testDir = tmpdir.path; tmpdir.refresh(); +(async () => { + // Watch a folder and update an already existing file in it. + + const rootDirectory = fs.mkdtempSync(testDir + path.sep); + const testDirectory = path.join(rootDirectory, 'test-0'); + fs.mkdirSync(testDirectory); + + const testFile = path.join(testDirectory, 'file-1.txt'); + fs.writeFileSync(testFile, 'hello'); + + const watcher = fs.watch(testDirectory, { recursive: true }); + let watcherClosed = false; + watcher.on('change', common.mustCallAtLeast(function(event, filename) { + // Libuv inconsistenly emits a rename event for the file we are watching + assert.ok(event === 'change' || event === 'rename'); + + if (filename === path.basename(testFile)) { + watcher.close(); + watcherClosed = true; + } + })); + + await setTimeout(common.platformTimeout(100)); + fs.writeFileSync(testFile, 'hello'); + + process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + }); +})().then(common.mustCall()); + + (async () => { // Add a file to already watching folder From efc44567c96b7237219372d3bc48c5bda5e93e0a Mon Sep 17 00:00:00 2001 From: MURAKAMI Masahiko Date: Fri, 18 Nov 2022 18:31:49 +0900 Subject: [PATCH 23/97] test_runner: add getter and setter to MockTracker This commit allows tests in test runner to use the `getter` and `setter` methods as "syntax sugar" for `MockTracker.method` with the `options.getter` or `options.setter` set to true in the options. Refs: https://github.com/nodejs/node/pull/45326#discussion_r1014727289 PR-URL: https://github.com/nodejs/node/pull/45506 Reviewed-By: Moshe Atlow Reviewed-By: Colin Ihrig Reviewed-By: Antoine du Hamel --- doc/api/test.md | 19 ++++++ lib/internal/test_runner/mock.js | 54 +++++++++++++++++ test/parallel/test-runner-mocking.js | 87 ++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+) diff --git a/doc/api/test.md b/doc/api/test.md index 6bfff0c341bfa7..3588c6d6acd45b 100644 --- a/doc/api/test.md +++ b/doc/api/test.md @@ -941,6 +941,15 @@ test('mocks a counting function', (t) => { }); ``` +### `mock.getter(object, methodName[, implementation][, options])` + + + +This function is syntax sugar for [`MockTracker.method`][] with `options.getter` +set to `true`. + ### `mock.method(object, methodName[, implementation][, options])` + +This function is syntax sugar for [`MockTracker.method`][] with `options.setter` +set to `true`. + ## Class: `TapStream` +The artifacts are built as part of release builds by running the [doc-upload](https://github.com/nodejs/node/blob/1a83ad6a693f851199608ae957ac5d4f76871485/Makefile#L1218-L1224) +Makefile target as part of the release-sources part of the +iojs+release job. +This target runs the `doc` target to build the docs and then uses +`scp` to copy them onto the staging/www server into a directory of the form +`/home/staging/nodejs///docs` where is e.g. +release, nightly, etc. The promotion step (either automatic for +nightlies or manual for releases) then moves the docs to +`/home/dist/nodejs/docs/\` where they are served by node.org. **The key things to know about the tooling include:** From 79edf257bb7d6877ecf82359bc636da02ab67892 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Mon, 28 Nov 2022 16:32:43 -0500 Subject: [PATCH 47/97] src: cleanup on disambiguating native modules Found while backporting https://github.com/nodejs/node/pull/45663 Fixup one rename missed Signed-off-by: Michael Dawson PR-URL: https://github.com/nodejs/node/pull/45665 Reviewed-By: Daeyeon Jeong Reviewed-By: Chengzhong Wu Reviewed-By: Yagiz Nizipli Reviewed-By: Mohammed Keyvanzadeh Reviewed-By: Anna Henningsen Reviewed-By: Luigi Pinca --- src/env.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/env.cc b/src/env.cc index be5d4b0723c578..129f070873517f 100644 --- a/src/env.cc +++ b/src/env.cc @@ -1580,11 +1580,11 @@ void Environment::PrintInfoForSnapshotIfDebug() { if (enabled_debug_list()->enabled(DebugCategory::MKSNAPSHOT)) { fprintf(stderr, "At the exit of the Environment:\n"); principal_realm()->PrintInfoForSnapshot(); - fprintf(stderr, "\nNative modules without cache:\n"); + fprintf(stderr, "\nBuiltins without cache:\n"); for (const auto& s : builtins_without_cache) { fprintf(stderr, "%s\n", s.c_str()); } - fprintf(stderr, "\nNative modules with cache:\n"); + fprintf(stderr, "\nBuiltins with cache:\n"); for (const auto& s : builtins_with_cache) { fprintf(stderr, "%s\n", s.c_str()); } From 418ae9be563ae99a8724e4cd661c128d4c1d6911 Mon Sep 17 00:00:00 2001 From: Facundo Tuesca Date: Sat, 3 Dec 2022 04:00:33 +0000 Subject: [PATCH 48/97] tools: remove dependency vulnerability checker This change removes the script used to check for new vulnerabilities in Node.js' dependencies, since it has been moved to its own repository. Refs: https://github.com/nodejs/nodejs-dependency-vuln-assessments PR-URL: https://github.com/nodejs/node/pull/45675 Reviewed-By: Rafael Gonzaga Reviewed-By: Michael Dawson Reviewed-By: Rich Trott Reviewed-By: Luigi Pinca Reviewed-By: Richard Lau --- tools/dep_checker/README.md | 72 ----------- tools/dep_checker/dependencies.py | 99 --------------- tools/dep_checker/main.py | 180 --------------------------- tools/dep_checker/requirements.txt | 3 - tools/dep_checker/versions_parser.py | 169 ------------------------- 5 files changed, 523 deletions(-) delete mode 100644 tools/dep_checker/README.md delete mode 100644 tools/dep_checker/dependencies.py delete mode 100644 tools/dep_checker/main.py delete mode 100644 tools/dep_checker/requirements.txt delete mode 100644 tools/dep_checker/versions_parser.py diff --git a/tools/dep_checker/README.md b/tools/dep_checker/README.md deleted file mode 100644 index 84e1fab640d992..00000000000000 --- a/tools/dep_checker/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# Node.js dependency vulnerability checker - -This script queries the [National Vulnerability Database (NVD)](https://nvd.nist.gov/) and -the [GitHub Advisory Database](https://github.com/advisories) for vulnerabilities found -in Node's dependencies. - -## How to use - -### Database authentication - -- In order to query the GitHub Advisory Database, - a [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) - has to be created (no permissions need to be given to the token, since it's only used to query the public database). -- The NVD can be queried without authentication, but it will be rate limited to one query every six seconds. In order to - remove - that limitation [request an API key](https://nvd.nist.gov/developers/request-an-api-key) and pass it as a parameter. - -### Running the script - -Once acquired, the script can be run as follows: - -```shell -cd node/tools/dep_checker/ -pip install -r requirements.txt - -# Python >= 3.9 required -python main.py --gh-token=$PERSONAL_ACCESS_TOKEN --nvd-key=$NVD_API_KEY - -# The command can also be run without parameters -# This will skip querying the GitHub Advisory Database, and query the NVD -# using the anonymous (rate-limited) API -python main.py -``` - -## Example output - -``` -WARNING: New vulnerabilities found -- npm (version 1.2.1) : - - GHSA-v3jv-wrf4-5845: https://github.com/advisories/GHSA-v3jv-wrf4-5845 - - GHSA-93f3-23rq-pjfp: https://github.com/advisories/GHSA-93f3-23rq-pjfp - - GHSA-m6cx-g6qm-p2cx: https://github.com/advisories/GHSA-m6cx-g6qm-p2cx - - GHSA-4328-8hgf-7wjr: https://github.com/advisories/GHSA-4328-8hgf-7wjr - - GHSA-x8qc-rrcw-4r46: https://github.com/advisories/GHSA-x8qc-rrcw-4r46 - - GHSA-m5h6-hr3q-22h5: https://github.com/advisories/GHSA-m5h6-hr3q-22h5 -- acorn (version 6.0.0) : - - GHSA-6chw-6frg-f759: https://github.com/advisories/GHSA-6chw-6frg-f759 - -For each dependency and vulnerability, check the following: -- Check the vulnerability's description to see if it applies to the dependency as -used by Node. If not, the vulnerability ID (either a CVE or a GHSA) can be added to the ignore list in -dependencies.py. IMPORTANT: Only do this if certain that the vulnerability found is a false positive. -- Otherwise, the vulnerability found must be remediated by updating the dependency in the Node repo to a -non-affected version. -``` - -## Implementation details - -- For each dependency in Node's `deps/` folder, the script parses their version number and queries the databases to find - vulnerabilities for that specific version. -- The queries can return false positives ( - see [this](https://github.com/nodejs/security-wg/issues/802#issuecomment-1144207417) comment for an example). These - can be ignored by adding the vulnerability to the `ignore_list` in `dependencies.py` -- If no NVD API key is provided, the script will take a while to finish (~2 min) because queries to the NVD - are [rate-limited](https://nvd.nist.gov/developers/start-here) -- If any vulnerabilities are found, the script returns 1 and prints out a list with the ID and a link to a description - of - the vulnerability. This is the case except when the ID matches one in the ignore-list (inside `dependencies.py`) in - which case the vulnerability is ignored. - - - diff --git a/tools/dep_checker/dependencies.py b/tools/dep_checker/dependencies.py deleted file mode 100644 index b0c6943aa42b0b..00000000000000 --- a/tools/dep_checker/dependencies.py +++ /dev/null @@ -1,99 +0,0 @@ -"""A list of dependencies, including their CPE, names and keywords for querying different vulnerability databases""" - -from typing import Optional -import versions_parser as vp - - -class CPE: - def __init__(self, vendor: str, product: str): - self.vendor = vendor - self.product = product - - -class Dependency: - def __init__( - self, - version: str, - cpe: Optional[CPE] = None, - npm_name: Optional[str] = None, - keyword: Optional[str] = None, - ): - self.version = version - self.cpe = cpe - self.npm_name = npm_name - self.keyword = keyword - - def get_cpe(self) -> Optional[str]: - if self.cpe: - return f"cpe:2.3:a:{self.cpe.vendor}:{self.cpe.product}:{self.version}:*:*:*:*:*:*:*" - else: - return None - - -ignore_list: list[str] = [ - "CVE-2018-25032", # zlib, already fixed in the fork Node uses (Chromium's) - "CVE-2007-5536", # openssl, old and only in combination with HP-UX - "CVE-2019-0190", # openssl, can be only triggered in combination with Apache HTTP Server version 2.4.37 -] - -dependencies: dict[str, Dependency] = { - "zlib": Dependency( - version=vp.get_zlib_version(), cpe=CPE(vendor="zlib", product="zlib") - ), - # TODO: Add V8 - # "V8": Dependency("cpe:2.3:a:google:chrome:*:*:*:*:*:*:*:*", "v8"), - "uvwasi": Dependency(version=vp.get_uvwasi_version(), cpe=None, keyword="uvwasi"), - "libuv": Dependency( - version=vp.get_libuv_version(), cpe=CPE(vendor="libuv_project", product="libuv") - ), - "undici": Dependency( - version=vp.get_undici_version(), - cpe=CPE(vendor="nodejs", product="undici"), - npm_name="undici", - ), - "OpenSSL": Dependency( - version=vp.get_openssl_version(), cpe=CPE(vendor="openssl", product="openssl") - ), - "npm": Dependency( - version=vp.get_npm_version(), - cpe=CPE(vendor="npmjs", product="npm"), - npm_name="npm", - ), - "nghttp3": Dependency( - version=vp.get_nghttp3_version(), cpe=None, keyword="nghttp3" - ), - "ngtcp2": Dependency(version=vp.get_ngtcp2_version(), cpe=None, keyword="ngtcp2"), - "nghttp2": Dependency( - version=vp.get_nghttp2_version(), cpe=CPE(vendor="nghttp2", product="nghttp2") - ), - "llhttp": Dependency( - version=vp.get_llhttp_version(), - cpe=CPE(vendor="llhttp", product="llhttp"), - npm_name="llhttp", - ), - "ICU": Dependency( - version=vp.get_icu_version(), - cpe=CPE(vendor="icu-project", product="international_components_for_unicode"), - ), - "HdrHistogram": Dependency(version="0.11.2", cpe=None, keyword="hdrhistogram"), - "corepack": Dependency( - version=vp.get_corepack_version(), - cpe=None, - keyword="corepack", - npm_name="corepack", - ), - "CJS Module Lexer": Dependency( - version=vp.get_cjs_lexer_version(), - cpe=None, - keyword="cjs-module-lexer", - npm_name="cjs-module-lexer", - ), - "c-ares": Dependency( - version=vp.get_c_ares_version(), - cpe=CPE(vendor="c-ares_project", product="c-ares"), - ), - "brotli": Dependency( - version=vp.get_brotli_version(), cpe=CPE(vendor="google", product="brotli") - ), - "acorn": Dependency(version=vp.get_acorn_version(), cpe=None, npm_name="acorn"), -} diff --git a/tools/dep_checker/main.py b/tools/dep_checker/main.py deleted file mode 100644 index 6675f48f570cfb..00000000000000 --- a/tools/dep_checker/main.py +++ /dev/null @@ -1,180 +0,0 @@ -""" Node.js dependency vulnerability checker - -This script queries the National Vulnerability Database (NVD) and the GitHub Advisory Database for vulnerabilities found -in Node's dependencies. - -For each dependency in Node's `deps/` folder, the script parses their version number and queries the databases to find -vulnerabilities for that specific version. - -If any vulnerabilities are found, the script returns 1 and prints out a list with the ID and a link to a description of -the vulnerability. This is the case except when the ID matches one in the ignore-list (inside `dependencies.py`) in -which case the vulnerability is ignored. -""" - -from argparse import ArgumentParser -from collections import defaultdict -from dependencies import ignore_list, dependencies -from gql import gql, Client -from gql.transport.aiohttp import AIOHTTPTransport -from nvdlib import searchCVE # type: ignore -from packaging.specifiers import SpecifierSet -from typing import Optional - - -class Vulnerability: - def __init__(self, id: str, url: str): - self.id = id - self.url = url - - -vulnerability_found_message = """For each dependency and vulnerability, check the following: -- Check that the dependency's version printed by the script corresponds to the version present in the Node repo. -If not, update dependencies.py with the actual version number and run the script again. -- If the version is correct, check the vulnerability's description to see if it applies to the dependency as -used by Node. If not, the vulnerability ID (either a CVE or a GHSA) can be added to the ignore list in -dependencies.py. IMPORTANT: Only do this if certain that the vulnerability found is a false positive. -- Otherwise, the vulnerability found must be remediated by updating the dependency in the Node repo to a -non-affected version, followed by updating dependencies.py with the new version. -""" - - -github_vulnerabilities_query = gql( - """ - query($package_name:String!) { - securityVulnerabilities(package:$package_name, last:10) { - nodes { - vulnerableVersionRange - advisory { - ghsaId - permalink - withdrawnAt - } - } - } - } -""" -) - - -def query_ghad(gh_token: str) -> dict[str, list[Vulnerability]]: - """Queries the GitHub Advisory Database for vulnerabilities reported for Node's dependencies. - - The database supports querying by package name in the NPM ecosystem, so we only send queries for the dependencies - that are also NPM packages. - """ - - deps_in_npm = { - name: dep for name, dep in dependencies.items() if dep.npm_name is not None - } - - transport = AIOHTTPTransport( - url="https://api.github.com/graphql", - headers={"Authorization": f"bearer {gh_token}"}, - ) - client = Client( - transport=transport, - fetch_schema_from_transport=True, - serialize_variables=True, - parse_results=True, - ) - - found_vulnerabilities: dict[str, list[Vulnerability]] = defaultdict(list) - for name, dep in deps_in_npm.items(): - variables_package = { - "package_name": dep.npm_name, - } - result = client.execute( - github_vulnerabilities_query, variable_values=variables_package - ) - matching_vulns = [ - v - for v in result["securityVulnerabilities"]["nodes"] - if v["advisory"]["withdrawnAt"] is None - and dep.version in SpecifierSet(v["vulnerableVersionRange"]) - and v["advisory"]["ghsaId"] not in ignore_list - ] - if matching_vulns: - found_vulnerabilities[name].extend( - [ - Vulnerability( - id=vuln["advisory"]["ghsaId"], url=vuln["advisory"]["permalink"] - ) - for vuln in matching_vulns - ] - ) - - return found_vulnerabilities - - -def query_nvd(api_key: Optional[str]) -> dict[str, list[Vulnerability]]: - """Queries the National Vulnerability Database for vulnerabilities reported for Node's dependencies. - - The database supports querying by CPE (Common Platform Enumeration) or by a keyword present in the CVE's - description. - Since some of Node's dependencies don't have an associated CPE, we use their name as a keyword in the query. - """ - deps_in_nvd = { - name: dep - for name, dep in dependencies.items() - if dep.cpe is not None or dep.keyword is not None - } - found_vulnerabilities: dict[str, list[Vulnerability]] = defaultdict(list) - for name, dep in deps_in_nvd.items(): - query_results = [ - cve - for cve in searchCVE( - cpeMatchString=dep.get_cpe(), keyword=dep.keyword, key=api_key - ) - if cve.id not in ignore_list - ] - if query_results: - found_vulnerabilities[name].extend( - [Vulnerability(id=cve.id, url=cve.url) for cve in query_results] - ) - - return found_vulnerabilities - - -def main(): - parser = ArgumentParser( - description="Query the NVD and the GitHub Advisory Database for new vulnerabilities in Node's dependencies" - ) - parser.add_argument( - "--gh-token", - help="the GitHub authentication token for querying the GH Advisory Database", - ) - parser.add_argument( - "--nvd-key", - help="the NVD API key for querying the National Vulnerability Database", - ) - gh_token = parser.parse_args().gh_token - nvd_key = parser.parse_args().nvd_key - if gh_token is None: - print( - "Warning: GitHub authentication token not provided, skipping GitHub Advisory Database queries" - ) - if nvd_key is None: - print( - "Warning: NVD API key not provided, queries will be slower due to rate limiting" - ) - ghad_vulnerabilities: dict[str, list[Vulnerability]] = ( - {} if gh_token is None else query_ghad(gh_token) - ) - nvd_vulnerabilities: dict[str, list[Vulnerability]] = query_nvd(nvd_key) - - if not ghad_vulnerabilities and not nvd_vulnerabilities: - print(f"No new vulnerabilities found ({len(ignore_list)} ignored)") - return 0 - else: - print("WARNING: New vulnerabilities found") - for source in (ghad_vulnerabilities, nvd_vulnerabilities): - for name, vulns in source.items(): - print(f"- {name} (version {dependencies[name].version}) :") - for v in vulns: - print(f"\t- {v.id}: {v.url}") - print(f"\n{vulnerability_found_message}") - return 1 - - -if __name__ == "__main__": - exit(main()) diff --git a/tools/dep_checker/requirements.txt b/tools/dep_checker/requirements.txt deleted file mode 100644 index 3a41c5824b7f06..00000000000000 --- a/tools/dep_checker/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -gql[aiohttp] -nvdlib==0.5.8 -packaging diff --git a/tools/dep_checker/versions_parser.py b/tools/dep_checker/versions_parser.py deleted file mode 100644 index 3a385bf1d2dd0b..00000000000000 --- a/tools/dep_checker/versions_parser.py +++ /dev/null @@ -1,169 +0,0 @@ -"""Utility functions to parse version numbers from each of Node's dependencies""" - -from pathlib import Path -import re - - -def get_package_json_version(path: Path) -> str: - with open(path, "r") as f: - matches = re.search('"version": "(?P.*)"', f.read()) - if matches is None: - raise RuntimeError(f"Error extracting version number from {path}") - return matches.groupdict()["version"] - - -def get_acorn_version() -> str: - return get_package_json_version(Path("../../deps/acorn/acorn/package.json")) - - -def get_brotli_version() -> str: - with open("../../deps/brotli/c/common/version.h", "r") as f: - matches = re.search("#define BROTLI_VERSION (?P.*)", f.read()) - if matches is None: - raise RuntimeError("Error extracting version number for brotli") - hex_version = matches.groupdict()["version"] - major_version = int(hex_version, 16) >> 24 - minor_version = int(hex_version, 16) >> 12 & 0xFF - patch_version = int(hex_version, 16) & 0xFFFFF - return f"{major_version}.{minor_version}.{patch_version}" - - -def get_c_ares_version() -> str: - with open("../../deps/cares/include/ares_version.h", "r") as f: - matches = re.search('#define ARES_VERSION_STR "(?P.*)"', f.read()) - if matches is None: - raise RuntimeError("Error extracting version number for c-ares") - return matches.groupdict()["version"] - - -def get_cjs_lexer_version() -> str: - return get_package_json_version(Path("../../deps/cjs-module-lexer/package.json")) - - -def get_corepack_version() -> str: - return get_package_json_version(Path("../../deps/corepack/package.json")) - - -def get_icu_version() -> str: - with open("../../deps/icu-small/source/common/unicode/uvernum.h", "r") as f: - matches = re.search('#define U_ICU_VERSION "(?P.*)"', f.read()) - if matches is None: - raise RuntimeError("Error extracting version number for ICU") - return matches.groupdict()["version"] - - -def get_llhttp_version() -> str: - with open("../../deps/llhttp/include/llhttp.h", "r") as f: - matches = re.search( - "#define LLHTTP_VERSION_MAJOR (?P.*)\n" - "#define LLHTTP_VERSION_MINOR (?P.*)\n" - "#define LLHTTP_VERSION_PATCH (?P.*)", - f.read(), - re.MULTILINE, - ) - if matches is None: - raise RuntimeError("Error extracting version number for llhttp") - versions = matches.groupdict() - return f"{versions['major']}.{versions['minor']}.{versions['patch']}" - - -def get_nghttp2_version() -> str: - with open("../../deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h", "r") as f: - matches = re.search('#define NGHTTP2_VERSION "(?P.*)"', f.read()) - if matches is None: - raise RuntimeError("Error extracting version number for nghttp2") - return matches.groupdict()["version"] - - -def get_ngtcp2_version() -> str: - with open("../../deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h", "r") as f: - matches = re.search('#define NGTCP2_VERSION "(?P.*)"', f.read()) - if matches is None: - raise RuntimeError("Error extracting version number for ngtcp2") - return matches.groupdict()["version"] - - -def get_nghttp3_version() -> str: - with open("../../deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h", "r") as f: - matches = re.search('#define NGHTTP3_VERSION "(?P.*)"', f.read()) - if matches is None: - raise RuntimeError("Error extracting version number for nghttp3") - return matches.groupdict()["version"] - - -def get_npm_version() -> str: - return get_package_json_version(Path("../../deps/npm/package.json")) - - -def get_openssl_version() -> str: - with open("../../deps/openssl/openssl/VERSION.dat", "r") as f: - matches = re.search( - "MAJOR=(?P.*)\n" "MINOR=(?P.*)\n" "PATCH=(?P.*)", - f.read(), - re.MULTILINE, - ) - if matches is None: - raise RuntimeError("Error extracting version number for openssl") - versions = matches.groupdict() - return f"{versions['major']}.{versions['minor']}.{versions['patch']}" - - -def get_undici_version() -> str: - return get_package_json_version(Path("../../deps/undici/src/package.json")) - - -def get_libuv_version() -> str: - with open("../../deps/uv/include/uv/version.h", "r") as f: - matches = re.search( - "#define UV_VERSION_MAJOR (?P.*)\n" - "#define UV_VERSION_MINOR (?P.*)\n" - "#define UV_VERSION_PATCH (?P.*)", - f.read(), - re.MULTILINE, - ) - if matches is None: - raise RuntimeError("Error extracting version number for libuv") - versions = matches.groupdict() - return f"{versions['major']}.{versions['minor']}.{versions['patch']}" - - -def get_uvwasi_version() -> str: - with open("../../deps/uvwasi/include/uvwasi.h", "r") as f: - matches = re.search( - "#define UVWASI_VERSION_MAJOR (?P.*)\n" - "#define UVWASI_VERSION_MINOR (?P.*)\n" - "#define UVWASI_VERSION_PATCH (?P.*)", - f.read(), - re.MULTILINE, - ) - if matches is None: - raise RuntimeError("Error extracting version number for uvwasi") - versions = matches.groupdict() - return f"{versions['major']}.{versions['minor']}.{versions['patch']}" - - -def get_v8_version() -> str: - with open("../../deps/v8/include/v8-version.h", "r") as f: - matches = re.search( - "#define V8_MAJOR_VERSION (?P.*)\n" - "#define V8_MINOR_VERSION (?P.*)\n" - "#define V8_BUILD_NUMBER (?P.*)\n" - "#define V8_PATCH_LEVEL (?P.*)\n", - f.read(), - re.MULTILINE, - ) - if matches is None: - raise RuntimeError("Error extracting version number for v8") - versions = matches.groupdict() - patch_suffix = "" if versions["patch"] == "0" else f".{versions['patch']}" - return ( - f"{versions['major']}.{versions['minor']}.{versions['build']}{patch_suffix}" - ) - - -def get_zlib_version() -> str: - with open("../../deps/zlib/zlib.h", "r") as f: - matches = re.search('#define ZLIB_VERSION "(?P.*)"', f.read()) - if matches is None: - raise RuntimeError("Error extracting version number for zlib") - return matches.groupdict()["version"] From 9f51b9e50d99b67d1289a9de468514edf5711f64 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sat, 3 Dec 2022 01:59:49 -0800 Subject: [PATCH 49/97] doc: add doc-only deprecation for headers/trailers setters The headers and trailers for http.IncomingMessage will be read-only in a future version of Node.js. Ref: https://github.com/nodejs/node/pull/45571 PR-URL: https://github.com/nodejs/node/pull/45697 Refs: https://github.com/nodejs/node/pull/45571 Reviewed-By: Antoine du Hamel Reviewed-By: Rafael Gonzaga Reviewed-By: Luigi Pinca --- doc/api/deprecations.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 83d2651c5cb7fb..8c1a1e5b161b9f 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -3308,6 +3308,22 @@ Type: Documentation-only might result in host name spoofing with unexpected input. These URLs will throw an error in future versions of Node.js, as the [WHATWG URL API][] does already. +### DEP0171: Setters for `http.IncomingMessage` headers and trailers + + + +Type: Documentation-only + +In a future version of Node.js, [`message.headers`][], +[`message.headersDistinct`][], [`message.trailers`][], and +[`message.trailersDistinct`][] will be read-only. + [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3 [RFC 8247 Section 2.4]: https://www.rfc-editor.org/rfc/rfc8247#section-2.4 @@ -3384,7 +3400,11 @@ an error in future versions of Node.js, as the [WHATWG URL API][] does already. [`https.get()`]: https.md#httpsgetoptions-callback [`https.request()`]: https.md#httpsrequestoptions-callback [`message.connection`]: http.md#messageconnection +[`message.headersDistinct`]: http.md#messageheadersdistinct +[`message.headers`]: http.md#messageheaders [`message.socket`]: http.md#messagesocket +[`message.trailersDistinct`]: http.md#messagetrailersdistinct +[`message.trailers`]: http.md#messagetrailers [`module.createRequire()`]: module.md#modulecreaterequirefilename [`os.networkInterfaces()`]: os.md#osnetworkinterfaces [`os.tmpdir()`]: os.md#ostmpdir From b8b13dccd982e733dbe806e157d401d9d9207b2f Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Sat, 3 Dec 2022 18:55:57 +0100 Subject: [PATCH 50/97] net: add autoSelectFamily and autoSelectFamilyAttemptTimeout options PR-URL: https://github.com/nodejs/node/pull/44731 Reviewed-By: Matteo Collina Reviewed-By: Robert Nagy Reviewed-By: Antoine du Hamel Reviewed-By: Rafael Gonzaga Reviewed-By: James M Snell Reviewed-By: Yagiz Nizipli --- doc/api/net.md | 18 ++ lib/_tls_wrap.js | 18 +- lib/internal/errors.js | 8 + lib/internal/net.js | 1 + lib/net.js | 255 +++++++++++++++++- test/parallel/test-http-happy-eyeballs.js | 148 ++++++++++ test/parallel/test-https-happy-eyeballs.js | 164 +++++++++++ .../test-net-happy-eyeballs-ipv4first.js | 112 ++++++++ test/parallel/test-net-happy-eyeballs.js | 215 +++++++++++++++ 9 files changed, 932 insertions(+), 7 deletions(-) create mode 100644 test/parallel/test-http-happy-eyeballs.js create mode 100644 test/parallel/test-https-happy-eyeballs.js create mode 100644 test/parallel/test-net-happy-eyeballs-ipv4first.js create mode 100644 test/parallel/test-net-happy-eyeballs.js diff --git a/doc/api/net.md b/doc/api/net.md index e6555e5415470f..c0cb1dbd7341c5 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -856,6 +856,9 @@ behavior. ISO 8601 format: YYYY-MM-DDTHH:mm:ss.sssZ +
  • expires: null or a simplified extended ISO 8601 format: YYYY-MM-DDTHH:mm:ss.sssZ
  • keydid: sha256 fingerprint of the public key
  • keytype: only ecdsa-sha2-nistp256 is currently supported by the npm CLI
  • scheme: only ecdsa-sha2-nistp256 is currently supported by the npm CLI
  • key: base64 encoded public key
  • -

    See this example key's response from the public npm registry.

    +

    See this example key's response from the public npm registry.

    Audit Endpoints

    There are two audit endpoints that npm may use to fetch vulnerability information: the Bulk Advisory endpoint and the Quick Audit endpoint.

    @@ -216,9 +216,9 @@

    Bulk Advisory Endpoint

    the path /-/npm/v1/security/advisories/bulk.

    Any packages in the tree that do not have a version field in their package.json file will be ignored. If any --omit options are specified -(either via the --omit config, or one of the shorthands such as ---production, --only=dev, and so on), then packages will be omitted -from the submitted payload as appropriate.

    +(either via the --omit config, or one of the +shorthands such as --production, --only=dev, and so on), then packages will +be omitted from the submitted payload as appropriate.

    If the registry responds with an error, or with an invalid response, then npm will attempt to load advisory data from the Quick Audit endpoint.

    The expected result will contain a set of advisory objects for each @@ -276,36 +276,36 @@

    Exit Code

    vulnerabilities are found or if the remediation is able to successfully fix all vulnerabilities.

    If vulnerabilities were found the exit code will depend on the -audit-level configuration setting.

    +audit-level config.

    Examples

    Scan your project for vulnerabilities and automatically install any compatible updates to vulnerable dependencies:

    -
    $ npm audit fix
    +
    $ npm audit fix
     

    Run audit fix without modifying node_modules, but still updating the pkglock:

    -
    $ npm audit fix --package-lock-only
    +
    $ npm audit fix --package-lock-only
     

    Skip updating devDependencies:

    -
    $ npm audit fix --only=prod
    +
    $ npm audit fix --only=prod
     

    Have audit fix install SemVer-major updates to toplevel dependencies, not just SemVer-compatible ones:

    -
    $ npm audit fix --force
    +
    $ npm audit fix --force
     

    Do a dry run to get an idea of what audit fix will do, and also output install information in JSON format:

    -
    $ npm audit fix --dry-run --json
    +
    $ npm audit fix --dry-run --json
     

    Scan your project for vulnerabilities and just show the details, without fixing anything:

    -
    $ npm audit
    +
    $ npm audit
     

    Get the detailed audit report in JSON format:

    -
    $ npm audit --json
    +
    $ npm audit --json
     

    Fail an audit only if the results include a vulnerability with a level of moderate or higher:

    -
    $ npm audit --audit-level=moderate
    +
    $ npm audit --audit-level=moderate
     

    Configuration

    audit-level

    @@ -454,18 +454,17 @@

    include-workspace-root

    This value is not exported to the environment for child processes.

      -
    • Default: false
    • +
    • Default: true
    • Type: Boolean
    -

    When set file: protocol dependencies that exist outside of the project root -will be packed and installed as regular dependencies instead of creating a -symlink. This option has no effect on workspaces.

    +

    When set file: protocol dependencies will be packed and installed as regular +dependencies instead of creating a symlink. This option has no effect on +workspaces.

    See Also

    - +