@@ -98,6 +98,87 @@ void requireValidHeaderValue(kj::StringPtr value) {
9898    JSG_REQUIRE (c != ' \0 ' ' \r ' ' \n ' " Invalid header value." 
9999  }
100100}
101+ 
102+ //  If any more headers are added to the CommonHeaderName enum later, we should be careful about
103+ //  introducing them into serialization. We need to roll out a change that recognizes the new IDs
104+ //  before rolling out a change that sends them. MAX_COMMON_HEADER_ID is the max value we're willing
105+ //  to send.
106+ static  constexpr  size_t  MAX_COMMON_HEADER_ID =
107+     static_cast <size_t >(capnp::CommonHeaderName::WWW_AUTHENTICATE);
108+ 
109+ //  Constexpr array of lowercase common header names (must match CommonHeaderName enum order
110+ //  and must be kept in sync with the ordinal values defined in http-over-capnp.capnp). Since
111+ //  it is extremely unlikely that those will change often, we hardcode them here for runtime
112+ //  efficiency.
113+ static  constexpr  const  char * COMMON_HEADER_NAMES[] = {
114+   nullptr ,                        //  0: invalid
115+   " accept-charset" //  1
116+   " accept-encoding" //  2
117+   " accept-language" //  3
118+   " accept-ranges" //  4
119+   " accept" //  5
120+   " access-control-allow-origin" //  6
121+   " age" //  7
122+   " allow" //  8
123+   " authorization" //  9
124+   " cache-control" //  10
125+   " content-disposition" //  11
126+   " content-encoding" //  12
127+   " content-language" //  13
128+   " content-length" //  14
129+   " content-location" //  15
130+   " content-range" //  16
131+   " content-type" //  17
132+   " cookie" //  18
133+   " date" //  19
134+   " etag" //  20
135+   " expect" //  21
136+   " expires" //  22
137+   " from" //  23
138+   " host" //  24
139+   " if-match" //  25
140+   " if-modified-since" //  26
141+   " if-none-match" //  27
142+   " if-range" //  28
143+   " if-unmodified-since" //  29
144+   " last-modified" //  30
145+   " link" //  31
146+   " location" //  32
147+   " max-forwards" //  33
148+   " proxy-authenticate" //  34
149+   " proxy-authorization" //  35
150+   " range" //  36
151+   " referer" //  37
152+   " refresh" //  38
153+   " retry-after" //  39
154+   " server" //  40
155+   " set-cookie" //  41
156+   " strict-transport-security" //  42
157+   " transfer-encoding" //  43
158+   " user-agent" //  44
159+   " vary" //  45
160+   " via" //  46
161+   " www-authenticate" //  47
162+ };
163+ 
164+ kj::String getCommonHeaderName (uint id) {
165+   KJ_ASSERT (id > 0  && id <= MAX_COMMON_HEADER_ID, " Invalid common header ID" 
166+   auto  name = COMMON_HEADER_NAMES[id];
167+   KJ_DASSERT (name != nullptr );
168+   return  kj::str (name);
169+ }
170+ 
171+ kj::Maybe<uint> getCommonHeaderId (kj::StringPtr name) {
172+   //  It really shouldn't be possible for a name to be empty but just in case...
173+   if  (name.size () == 0 ) return  kj::none;
174+   for  (uint i = 1 ; i <= MAX_COMMON_HEADER_ID; ++i) {
175+     KJ_DASSERT (COMMON_HEADER_NAMES[i] != nullptr );
176+     if  (name == COMMON_HEADER_NAMES[i]) return  i;
177+   }
178+   return  kj::none;
179+ }
180+ 
181+ static_assert (std::size(COMMON_HEADER_NAMES) == (MAX_COMMON_HEADER_ID + 1 ));
101182}  //  namespace
102183
103184Headers::Headers (jsg::Lock& js, jsg::Dict<jsg::ByteString, jsg::ByteString> dict)
@@ -416,78 +497,6 @@ bool Headers::inspectImmutable() {
416497//  capitalization). So, it's certainly not worth it to try to keep the original capitalization
417498//  across serialization.
418499
419- //  If any more headers are added to the CommonHeaderName enum later, we should be careful about
420- //  introducing them into serialization. We need to roll out a change that recognizes the new IDs
421- //  before rolling out a change that sends them. MAX_COMMON_HEADER_ID is the max value we're willing
422- //  to send.
423- static  constexpr  uint MAX_COMMON_HEADER_ID =
424-     static_cast <uint>(capnp::CommonHeaderName::WWW_AUTHENTICATE);
425- 
426- //  ID for the `$commonText` annotation declared in http-over-capnp.capnp.
427- //  TODO(cleanup): Cap'n Proto should really codegen constants for annotation IDs so we don't have
428- //    to copy them.
429- static  constexpr  uint64_t  COMMON_TEXT_ANNOTATION_ID = 0x857745131db6fc83 ;
430- 
431- static  kj::Array<kj::StringPtr> makeCommonHeaderList () {
432-   auto  enums = capnp::Schema::from<capnp::CommonHeaderName>().getEnumerants ();
433-   auto  builder = kj::heapArrayBuilder<kj::StringPtr>(enums.size ());
434-   bool  first = true ;
435-   for  (auto  e: enums) {
436-     if  (first) {
437-       //  Value zero is invalid, skip it.
438-       static_assert (static_cast <uint>(capnp::CommonHeaderName::INVALID) == 0 );
439- 
440-       //  Add `nullptr` to the array so that our array indexes aren't off-by-one from the enum
441-       //  values. We could in theory skip this and use +1 and -1 in a bunch of places but that seems
442-       //  error-prone.
443-       builder.add (nullptr );
444- 
445-       first = false ;
446-       continue ;
447-     }
448- 
449-     kj::Maybe<kj::StringPtr> name;
450- 
451-     //  Look for $commonText annotation.
452-     for  (auto  ann: e.getProto ().getAnnotations ()) {
453-       if  (ann.getId () == COMMON_TEXT_ANNOTATION_ID) {
454-         name = ann.getValue ().getText ();
455-         break ;
456-       }
457-     }
458- 
459-     builder.add (KJ_ASSERT_NONNULL (name));
460-   }
461- 
462-   return  builder.finish ();
463- }
464- 
465- static  kj::ArrayPtr<const  kj::StringPtr> getCommonHeaderList () {
466-   static  const  kj::Array<kj::StringPtr> LIST = makeCommonHeaderList ();
467-   return  LIST;
468- }
469- 
470- static  kj::HashMap<kj::String, uint> makeCommonHeaderMap () {
471-   kj::HashMap<kj::String, uint> result;
472-   auto  list = getCommonHeaderList ();
473-   KJ_ASSERT (MAX_COMMON_HEADER_ID < list.size ());
474-   for  (auto  i: kj::range (1 , MAX_COMMON_HEADER_ID + 1 )) {
475-     auto  key = kj::str (list[i]);
476-     for  (auto & c: key) {
477-       if  (' A' ' Z' 
478-         c = c - ' A' ' a' 
479-       }
480-     }
481-     result.insert (kj::mv (key), i);
482-   }
483-   return  result;
484- }
485- 
486- static  const  kj::HashMap<kj::String, uint>& getCommonHeaderMap () {
487-   static  const  kj::HashMap<kj::String, uint> MAP = makeCommonHeaderMap ();
488-   return  MAP;
489- }
490- 
491500void  Headers::serialize (jsg::Lock& js, jsg::Serializer& serializer) {
492501  //  We serialize as a series of key-value pairs. Each value is a length-delimited string. Each key
493502  //  is a common header ID, or the value zero to indicate an uncommon header, which is then
@@ -503,10 +512,9 @@ void Headers::serialize(jsg::Lock& js, jsg::Serializer& serializer) {
503512  serializer.writeRawUint32 (count);
504513
505514  //  Now write key/values.
506-   auto & commonHeaders = getCommonHeaderMap ();
507515  for  (auto & entry: headers) {
508516    auto & header = entry.second ;
509-     auto  commonId = commonHeaders. find (header.key );
517+     auto  commonId = getCommonHeaderId (header.key );
510518    for  (auto & value: header.values ) {
511519      KJ_IF_SOME (c, commonId) {
512520        serializer.writeRawUint32 (c);
@@ -527,15 +535,14 @@ jsg::Ref<Headers> Headers::deserialize(
527535
528536  uint count = deserializer.readRawUint32 ();
529537
530-   auto  commonHeaders = getCommonHeaderList ();
531538  for  (auto  i KJ_UNUSED: kj::zeroTo (count)) {
532539    uint commonId = deserializer.readRawUint32 ();
533540    kj::String name;
534541    if  (commonId == 0 ) {
535542      name = deserializer.readLengthDelimitedString ();
536543    } else  {
537-       KJ_ASSERT (commonId < commonHeaders. size () );
538-       name = kj::str (commonHeaders[ commonId] );
544+       KJ_ASSERT (commonId <= MAX_COMMON_HEADER_ID );
545+       name = getCommonHeaderName ( commonId);
539546    }
540547
541548    auto  value = deserializer.readLengthDelimitedString ();
0 commit comments