@@ -33,33 +33,6 @@ struct CachedHeaderBatch {
3333static HEADER_BATCH_CACHE : OnceLock < Mutex < HashMap < HeaderBatchCacheKey , CachedHeaderBatch > > > =
3434 OnceLock :: new ( ) ;
3535
36- pub ( super ) fn parse_header_name ( name : impl AsRef < str > ) -> Result < HeaderName , VmError > {
37- let name = name. as_ref ( ) ;
38- HeaderName :: from_bytes ( name. as_bytes ( ) )
39- . map_err ( |_| VmError :: HostError ( format ! ( "invalid header name '{name}'" ) ) )
40- }
41-
42- pub ( super ) fn parse_header (
43- name : impl AsRef < str > ,
44- value : impl AsRef < str > ,
45- ) -> Result < ( HeaderName , HeaderValue ) , VmError > {
46- let name = name. as_ref ( ) ;
47- let value = value. as_ref ( ) ;
48- let header_name = HeaderName :: from_bytes ( name. as_bytes ( ) )
49- . map_err ( |_| VmError :: HostError ( format ! ( "invalid header name '{name}'" ) ) ) ?;
50- let header_value = HeaderValue :: from_str ( value)
51- . map_err ( |_| VmError :: HostError ( format ! ( "invalid header value '{value}'" ) ) ) ?;
52- Ok ( ( header_name, header_value) )
53- }
54-
55- pub ( super ) fn request_path_with_query ( path : & str , query : & str ) -> String {
56- if query. is_empty ( ) {
57- path. to_string ( )
58- } else {
59- format ! ( "{path}?{query}" )
60- }
61- }
62-
6336fn header_batch_cache ( ) -> & ' static Mutex < HashMap < HeaderBatchCacheKey , CachedHeaderBatch > > {
6437 HEADER_BATCH_CACHE . get_or_init ( || Mutex :: new ( HashMap :: new ( ) ) )
6538}
@@ -92,7 +65,7 @@ fn cached_header_batch_matches(source: &HeaderBatchCacheSource, headers: &Value)
9265 }
9366}
9467
95- pub ( super ) fn lookup_cached_header_batch ( headers : & Value ) -> Option < HeaderMap > {
68+ fn lookup_cached_header_batch ( headers : & Value ) -> Option < HeaderMap > {
9669 let key = header_batch_cache_key ( headers) ?;
9770 let mut guard = header_batch_cache ( ) . lock ( ) ;
9871 let cached = guard. get ( & key) ?;
@@ -103,7 +76,7 @@ pub(super) fn lookup_cached_header_batch(headers: &Value) -> Option<HeaderMap> {
10376 None
10477}
10578
106- pub ( super ) fn store_cached_header_batch ( headers : & Value , parsed : & HeaderMap ) {
79+ fn store_cached_header_batch ( headers : & Value , parsed : & HeaderMap ) {
10780 let ( Some ( key) , Some ( source) ) = (
10881 header_batch_cache_key ( headers) ,
10982 header_batch_cache_source ( headers) ,
@@ -132,6 +105,86 @@ pub(super) fn store_cached_header_batch(headers: &Value, parsed: &HeaderMap) {
132105 ) ;
133106}
134107
108+ pub ( super ) fn parse_string_header_batch (
109+ headers : Value ,
110+ batch_name : & ' static str ,
111+ ) -> Result < HeaderMap , VmError > {
112+ match headers {
113+ Value :: Null => Ok ( HeaderMap :: new ( ) ) ,
114+ Value :: Array ( values) => {
115+ if let Some ( parsed) = lookup_cached_header_batch ( & Value :: Array ( values. clone ( ) ) ) {
116+ return Ok ( parsed) ;
117+ }
118+ if values. len ( ) % 2 != 0 {
119+ return Err ( VmError :: HostError ( format ! (
120+ "{batch_name} arrays must contain alternating name/value string pairs" ,
121+ ) ) ) ;
122+ }
123+ let mut parsed = HeaderMap :: new ( ) ;
124+ for pair in values. chunks ( 2 ) {
125+ let Value :: String ( name) = & pair[ 0 ] else {
126+ return Err ( VmError :: HostError ( format ! (
127+ "{batch_name} array keys must be strings" ,
128+ ) ) ) ;
129+ } ;
130+ let Value :: String ( value) = & pair[ 1 ] else {
131+ return Err ( VmError :: HostError ( format ! (
132+ "{batch_name} array values must be strings" ,
133+ ) ) ) ;
134+ } ;
135+ let ( header_name, header_value) = parse_header ( name. as_str ( ) , value. as_str ( ) ) ?;
136+ parsed. insert ( header_name, header_value) ;
137+ }
138+ store_cached_header_batch ( & Value :: Array ( values. clone ( ) ) , & parsed) ;
139+ Ok ( parsed)
140+ }
141+ Value :: Map ( entries) => {
142+ if let Some ( parsed) = lookup_cached_header_batch ( & Value :: Map ( entries. clone ( ) ) ) {
143+ return Ok ( parsed) ;
144+ }
145+ let mut parsed = HeaderMap :: new ( ) ;
146+ for ( key, value) in entries. as_ref ( ) {
147+ let Value :: String ( name) = key else {
148+ return Err ( VmError :: HostError ( format ! (
149+ "{batch_name} map keys must be strings" ,
150+ ) ) ) ;
151+ } ;
152+ let Value :: String ( value) = value else {
153+ return Err ( VmError :: HostError ( format ! (
154+ "{batch_name} map values must be strings" ,
155+ ) ) ) ;
156+ } ;
157+ let ( header_name, header_value) = parse_header ( name. as_str ( ) , value. as_str ( ) ) ?;
158+ parsed. insert ( header_name, header_value) ;
159+ }
160+ store_cached_header_batch ( & Value :: Map ( entries. clone ( ) ) , & parsed) ;
161+ Ok ( parsed)
162+ }
163+ _ => Err ( VmError :: HostError ( format ! (
164+ "{batch_name} must be null, an array of alternating strings, or a string map" ,
165+ ) ) ) ,
166+ }
167+ }
168+
169+ pub ( super ) fn parse_header_name ( name : impl AsRef < str > ) -> Result < HeaderName , VmError > {
170+ let name = name. as_ref ( ) ;
171+ HeaderName :: from_bytes ( name. as_bytes ( ) )
172+ . map_err ( |_| VmError :: HostError ( format ! ( "invalid header name '{name}'" ) ) )
173+ }
174+
175+ pub ( super ) fn parse_header (
176+ name : impl AsRef < str > ,
177+ value : impl AsRef < str > ,
178+ ) -> Result < ( HeaderName , HeaderValue ) , VmError > {
179+ let name = name. as_ref ( ) ;
180+ let value = value. as_ref ( ) ;
181+ let header_name = HeaderName :: from_bytes ( name. as_bytes ( ) )
182+ . map_err ( |_| VmError :: HostError ( format ! ( "invalid header name '{name}'" ) ) ) ?;
183+ let header_value = HeaderValue :: from_str ( value)
184+ . map_err ( |_| VmError :: HostError ( format ! ( "invalid header value '{value}'" ) ) ) ?;
185+ Ok ( ( header_name, header_value) )
186+ }
187+
135188pub ( super ) fn headers_to_value_map ( headers : & HeaderMap ) -> Value {
136189 let mut values = BTreeMap :: < String , Vec < String > > :: new ( ) ;
137190 for ( name, value) in headers {
@@ -154,6 +207,14 @@ pub(super) fn headers_to_value_map(headers: &HeaderMap) -> Value {
154207 )
155208}
156209
210+ pub ( super ) fn request_path_with_query ( path : & str , query : & str ) -> String {
211+ if query. is_empty ( ) {
212+ path. to_string ( )
213+ } else {
214+ format ! ( "{path}?{query}" )
215+ }
216+ }
217+
157218pub ( super ) fn query_to_value_map ( query : & str ) -> Value {
158219 let mut values = BTreeMap :: < String , Vec < String > > :: new ( ) ;
159220 for ( name, value) in url:: form_urlencoded:: parse ( query. as_bytes ( ) ) {
0 commit comments