package dream
Tidy, feature-complete Web framework
Install
Dune Dependency
Authors
Maintainers
Sources
dream-1.0.0-alpha2.tar.gz
sha256=bf19c13f0da7e01cc30b8dfaa1fca1dc8b44a06fb6e0eef56d56513952b9bb01
md5=1220f17530522e488653eb91115867e3
doc/src/dream.hpack/encoder.ml.html
Source file encoder.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
(*---------------------------------------------------------------------------- * Copyright (c) 2019 António Nuno Monteiro * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. *---------------------------------------------------------------------------*) open Types module IntSet = Set.Make (struct type t = int let compare = compare end) module HeaderFieldsTbl = struct include Hashtbl.Make (struct type t = string let equal = String.equal let hash s = Hashtbl.hash s end) let[@inline] find_opt h key = try Some (find h key) with Not_found -> None end module ValueMap = struct include Map.Make (String) let[@inline] find_opt k m = try Some (find k m) with Not_found -> None end type t = { table : Dynamic_table.t (* We maintain a lookup table of header fields to their indexes in the * dynamic table. The format is name -> (value -> index) *) ; lookup_table : int ValueMap.t HeaderFieldsTbl.t ; mutable next_seq : int } module BinaryFormat = struct (* From RFC7541§6.2.3. Literal Header Field Never Indexed * A literal header field never-indexed representation starts with the * '0001' 4-bit pattern. *) let never_indexed = 0b0001_0000, 4 (* From RFC7541§6.2.2: Literal Header Field without Indexing * A literal header field without indexing representation starts with the * '0000' 4-bit pattern. *) let without_indexing = 0b0000_0000, 4 (* From RFC7541§6.2.1: Literal Header Field with Incremental Indexing * A literal header field with incremental indexing representation starts * with the '01' 2-bit pattern. *) let incremental_indexing = 0b0100_0000, 6 (* From RFC7541§6.1: Indexed Header Field Representation * An indexed header field starts with the '1' 1-bit pattern, followed by * the index of the matching header field, represented as an integer with * a 7-bit prefix (see Section 5.1). *) let indexed = 0b1000_0000, 7 let[@inline] is_indexed = function 128 -> true | _ -> false end let on_evict lookup_table (name, value) = let map = HeaderFieldsTbl.find lookup_table name in if ValueMap.cardinal map = 1 then HeaderFieldsTbl.remove lookup_table name else let map = ValueMap.remove value map in HeaderFieldsTbl.replace lookup_table name map let create capacity = let lookup_table = HeaderFieldsTbl.create capacity in { table = Dynamic_table.create ~on_evict:(on_evict lookup_table) capacity ; lookup_table ; next_seq = 0 } let add ({ table; lookup_table; next_seq } as encoder) entry = let name, value = entry in Dynamic_table.add table entry; let map = match HeaderFieldsTbl.find_opt lookup_table name with | Some map -> ValueMap.add value next_seq map | None -> ValueMap.singleton value next_seq in encoder.next_seq <- next_seq + 1; HeaderFieldsTbl.replace lookup_table name map let[@inline] find_token encoder without_indexing token name value = let rec loop i = let name', value' = Static_table.table.(i) in if name' = name then if value' = value then (* From RFC7541§6.1: Indexed Header Field Representation * An indexed header field starts with the '1' 1-bit pattern, * followed by the index of the matching header field. *) BinaryFormat.indexed, i + 1 else (* Advance one token in the static table, as the next entry might have * a value that can fall into the above branch. We're guaranteed to * always get the first token (index) in the static table for `name`, * because that's what `Static_table.lookup_token` returns. *) loop (i + 1) else (* This is a header field whose value we didn't find in the static table * after looping. We ended here (name <> name') because we looped to * check whether the value was indexed in the static table. We can still * use the token index to encode the header name. *) let index = token + 1 in if without_indexing then (* From RFC7541§6.2.2: Literal Header Field without Indexing * If the header field name matches the header field name of an entry * stored in the static table or the dynamic table, the header field * name can be represented using the index of that entry. *) BinaryFormat.without_indexing, index else ( (* From RFC7541§6.2.1: Literal Header Field with Incremental Indexing * A literal header field with incremental indexing representation * results in appending a header field to the decoded header list and * inserting it as a new entry into the dynamic table. *) add encoder (name, value); BinaryFormat.incremental_indexing, index) in loop token let[@inline] seq_to_index next_seq seq = Static_table.table_size + next_seq - seq let tokens_without_indexing = (* From RFC7541§6.2.2: Never-Indexed Literals * Either form of header field name representation is followed by the * header field value represented as a string literal (see Section 5.2). * * Note: we choose not to index the values of these fields as they would * vary immensely. This way, we save some additions / evictions from the * dynamic table. *) IntSet.of_list Static_table.TokenIndices. [ path ; age ; content_length ; etag ; if_modified_since ; if_none_match ; location ; set_cookie ] let[@inline] is_without_indexing token = token <> -1 && IntSet.mem token tokens_without_indexing let[@inline] is_sensitive token value = token <> -1 && (* From RFC7541§7.1.3: Never-Indexed Literals * An encoder might also choose not to index values for header fields * that are considered to be highly valuable or sensitive to recovery, * such as the Cookie or Authorization header fields. *) Static_table.TokenIndices.( token == authorization || (token == cookie && String.length value < 20)) let encode ({ lookup_table; next_seq; _ } as encoder) { name; value; sensitive } = let token = Static_table.lookup_token_index name in let token_found_in_static_table = token <> -1 in if sensitive || is_sensitive token value then (* never indexed literal header field, find the index *) let index = if token_found_in_static_table then (* From RFC7541§6.2.2: Literal Header Field without Indexing * If the header field name matches the header field name of an entry * stored in the static table or the dynamic table, the header field * name can be represented using the index of that entry. *) token + 1 else match HeaderFieldsTbl.find_opt lookup_table name with | Some map -> let _, any_entry = ValueMap.choose map in seq_to_index next_seq any_entry | None -> (* From RFC7541§6.2.2: Literal Header Field without Indexing * Otherwise, the header field name is represented as a string * literal (see Section 5.2). A value 0 is used in place of the * 4-bit index, followed by the header field name. *) 0 in BinaryFormat.never_indexed, index else if token_found_in_static_table then (* Header name is represented in the static table. *) match HeaderFieldsTbl.find_opt lookup_table name with | Some map -> (* Header value is indexed in the dynamic table. *) (match ValueMap.find_opt value map with | Some seq -> (* From RFC7541§6.1: Indexed Header Field Representation * An indexed header field representation identifies an entry in * either the static table or the dynamic table (see Section 2.3). *) BinaryFormat.indexed, seq_to_index next_seq seq | None -> (* Header value is not indexed in the dynamic table. Check if it's an * entry in the static table or if we need to encode its value, (and * potentially name if the field is requested to be encoded without * indexing). *) let without_indexing = is_without_indexing token in find_token encoder without_indexing token name value) | None -> let without_indexing = is_without_indexing token in find_token encoder without_indexing token name value else match HeaderFieldsTbl.find_opt lookup_table name with | Some map -> (match ValueMap.find_opt value map with | Some seq -> BinaryFormat.indexed, seq_to_index next_seq seq | None -> let index = seq_to_index next_seq (snd (ValueMap.choose map)) in if is_without_indexing token then BinaryFormat.without_indexing, index else ( (* From RFC7541§6.2.1 * A literal header field with incremental indexing representation * results in appending a header field to the decoded header list * and inserting it as a new entry into the dynamic table. *) add encoder (name, value); BinaryFormat.incremental_indexing, index)) | None -> if is_without_indexing token then BinaryFormat.without_indexing, 0 else ( (* From RFC7541§6.2.1 * A literal header field with incremental indexing representation * results in appending a header field to the decoded header list and * inserting it as a new entry into the dynamic table. *) add encoder (name, value); BinaryFormat.incremental_indexing, 0) let[@inline] encode_int t prefix n i = let max_prefix = (1 lsl n) - 1 in if i < max_prefix then (* From RFC7541§5.1: * If the integer value is small enough, i.e., strictly less than 2^N-1, * it is encoded within the N-bit prefix. *) Faraday.write_uint8 t (prefix lor i) else (* From RFC7541§5.1: * Otherwise, all the bits of the prefix are set to 1, and the value, * decreased by 2^N-1, is encoded using a list of one or more octets. The * most significant bit of each octet is used as a continuation flag: its * value is set to 1 except for the last octet in the list. The remaining * bits of the octets are used to encode the decreased value. *) let i = i - max_prefix in Faraday.write_uint8 t (prefix lor max_prefix); let rec loop i = if i >= 128 then ( Faraday.write_uint8 t (i land 127 lor 128); loop (i lsr 7)) else Faraday.write_uint8 t i in loop i let[@inline] encode_string t s = let string_length = String.length s in let huffman_length = Huffman.encoded_length s in if huffman_length > string_length then ( (* From RFC7541§5.2: * The number of octets used to encode the string literal, encoded as an * integer with a 7-bit prefix (see Section 5.1). *) encode_int t 0 7 string_length; (* From RFC7541§5.2: * The encoded data of the string literal. If H is '0', then the encoded * data is the raw octets of the string literal. If H is '1', then the * encoded data is the Huffman encoding of the string literal. *) Faraday.write_string t s) else ( (* From RFC7541§5.2: * The number of octets used to encode the string literal, encoded as an * integer with a 7-bit prefix (see Section 5.1). *) encode_int t 128 7 huffman_length; (* From RFC7541§5.2: * The encoded data of the string literal. If H is '0', then the encoded * data is the raw octets of the string literal. If H is '1', then the * encoded data is the Huffman encoding of the string literal. *) Huffman.encode t s) let encode_header encoder t ({ name; value; _ } as header) = let (prefix, prefix_length), index = encode encoder header in encode_int t prefix prefix_length index; if not (BinaryFormat.is_indexed prefix) then ( if index == 0 then (* From RFC7541§6.2.2: Literal Header Field without Indexing * If the header field name matches the header field name of an entry * stored in the static table or the dynamic table, the header field * name can be represented using the index of that entry. In this case, * the index of the entry is represented as an integer with a 4-bit * prefix (see Section 5.1). This value is always non-zero. * * Otherwise, the header field name is represented as a string literal * (see Section 5.2). A value 0 is used in place of the 4-bit index, * followed by the header field name. *) encode_string t name; (* From RFC7541§6.2.2: Literal Header Field without Indexing * Either form of header field name representation is followed by the * header field value represented as a string literal (see * Section 5.2). *) encode_string t value) let set_capacity { table; _ } new_capacity = Dynamic_table.set_capacity table new_capacity
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>