11#include " headers.h"
22
3+ #include " simdutf.h"
34#include " util.h"
45
56#include < workerd/io/features.h>
@@ -70,14 +71,6 @@ constexpr size_t MAX_COMMON_HEADER_ID =
7071// and must be kept in sync with the ordinal values defined in http-over-capnp.capnp). Since
7172// it is extremely unlikely that those will change often, we hardcode them here for runtime
7273// efficiency.
73- //
74- // TODO(perf): We can potentially optimize this further by using the mechanisms within
75- // http-over-capnp, which also has a mapping of common header names to kj::HttpHeaderIds.
76- // However, accessing that functionality requires some amount of new API to be added to
77- // capnproto which needs to be carefully weighed. There's also the fact that, currently,
78- // the HttpOverCapnpFactory is accessed via IoContext and the Headers object can be
79- // created outside of an IoContext. Some amount of additional refactoring would be needed
80- // to make it work. For now, this hardcoded table is sufficient and efficient enough.
8174#define V (Name ) Name##_kj,
8275constexpr kj::StringPtr COMMON_HEADER_NAMES[] = {nullptr , // 0: invalid
8376 COMMON_HEADERS (V)};
@@ -164,16 +157,6 @@ static_assert(HEADER_HASH_TABLE.find("AcCePt-ChArSeT"_kj) == 1);
164157
165158static_assert (std::size(COMMON_HEADER_NAMES) == (MAX_COMMON_HEADER_ID + 1 ));
166159
167- inline constexpr void requireValidHeaderName (kj::StringPtr name) {
168- if (HEADER_HASH_TABLE.find (name) != 0 ) {
169- // Known common header, always valid
170- return ;
171- }
172- for (char c: name) {
173- JSG_REQUIRE (util::isHttpTokenChar (c), TypeError, " Invalid header name." );
174- }
175- }
176-
177160void maybeWarnIfBadHeaderString (kj::StringPtr str) {
178161 if (IoContext::hasCurrent ()) {
179162 auto & context = IoContext::current ();
@@ -241,6 +224,10 @@ constexpr Headers::HeaderKey getHeaderKeyFor(kj::StringPtr name) {
241224 return commonId;
242225 }
243226
227+ for (char c: name) {
228+ JSG_REQUIRE (util::isHttpTokenChar (c), TypeError, " Invalid header name." );
229+ }
230+
244231 // Not a common header, so allocate lowercase copy for uncommon header
245232 return toLower (name);
246233}
@@ -336,6 +323,9 @@ Headers::Headers(jsg::Lock& js, const Headers& other): guard(Guard::NONE) {
336323
337324Headers::Headers (jsg::Lock& js, const kj::HttpHeaders& other, Guard guard): guard(Guard::NONE) {
338325 headers.reserve (other.size ());
326+ // TODO(perf): Once kj::HttpHeaders supports an API for getting the CommonHeaderName directly
327+ // from the headers, we can optimize this to avoid looking up the common header IDs again,
328+ // making this constructor more efficient when copying common headers from kj::HttpHeaders.
339329 other.forEach ([this , &js](auto name, auto value) {
340330 // We have to copy the strings here but we can avoid normalizing and validating since
341331 // they presumably already went through that process when they were added to the
@@ -355,6 +345,8 @@ jsg::Ref<Headers> Headers::clone(jsg::Lock& js) const {
355345// Fill in the given HttpHeaders with these headers. Note that strings are inserted by
356346// reference, so the output must be consumed immediately.
357347void Headers::shallowCopyTo (kj::HttpHeaders& out) {
348+ // TODO(perf): Once kj::HttpHeaders supports an API for setting headers by CommonHeaderName,
349+ // we can optimize this to avoid the additional lookup of the header name and use of addPtrPtr.
358350 for (auto & entry: headers) {
359351 for (auto & value: entry.values ) {
360352 out.addPtrPtr (entry.getHeaderName (), value);
@@ -463,8 +455,7 @@ jsg::Ref<Headers> Headers::constructor(jsg::Lock& js, jsg::Optional<Initializer>
463455}
464456
465457kj::Maybe<kj::String> Headers::get (jsg::Lock& js, kj::String name) {
466- requireValidHeaderName (name);
467- return getUnguarded (js, name.asPtr ());
458+ return getUnguarded (js, name);
468459}
469460
470461kj::Maybe<kj::String> Headers::getUnguarded (jsg::Lock&, kj::StringPtr name) {
@@ -490,8 +481,6 @@ kj::Array<kj::StringPtr> Headers::getSetCookie() {
490481}
491482
492483kj::Array<kj::StringPtr> Headers::getAll (kj::String name) {
493- requireValidHeaderName (name);
494-
495484 if (!strcaseeq (name, " set-cookie" _kj)) {
496485 JSG_FAIL_REQUIRE (TypeError, " getAll() can only be used with the header name \" Set-Cookie\" ." );
497486 }
@@ -503,7 +492,6 @@ kj::Array<kj::StringPtr> Headers::getAll(kj::String name) {
503492}
504493
505494bool Headers::has (kj::String name) {
506- requireValidHeaderName (name);
507495 return headers.find (getHeaderKeyFor (name)) != kj::none;
508496}
509497
@@ -514,7 +502,6 @@ bool Headers::hasCommon(capnp::CommonHeaderName idx) {
514502
515503void Headers::set (jsg::Lock& js, kj::String name, kj::String value) {
516504 checkGuard ();
517- requireValidHeaderName (name);
518505 setUnguarded (js, kj::mv (name), normalizeHeaderValue (kj::mv (value)));
519506}
520507
@@ -542,7 +529,6 @@ void Headers::setCommon(capnp::CommonHeaderName idx, kj::String value) {
542529
543530void Headers::append (jsg::Lock& js, kj::String name, kj::String value) {
544531 checkGuard ();
545- requireValidHeaderName (name);
546532 appendUnguarded (js, kj::mv (name), normalizeHeaderValue (kj::mv (value)));
547533}
548534
@@ -561,7 +547,6 @@ void Headers::appendUnguarded(jsg::Lock& js, kj::String name, kj::String value)
561547
562548void Headers::delete_ (kj::String name) {
563549 checkGuard ();
564- requireValidHeaderName (name);
565550 headers.eraseMatch (getHeaderKeyFor (name));
566551}
567552
0 commit comments