123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218(*----------------------------------------------------------------------------
* 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.
*---------------------------------------------------------------------------*)openTypesopenAngstromtypet={table:Dynamic_table.t;max_capacity:int}letcreatemax_capacity={table=Dynamic_table.createmax_capacity;max_capacity}letset_capacity{table;max_capacity}capacity=ifcapacity>max_capacitythen(* From RFC7541§6.3:
* The new maximum size MUST be lower than or equal to the limit
* determined by the protocol using HPACK. A value that exceeds this
* limit MUST be treated as a decoding error. *)ErrorDecoding_errorelse(Dynamic_table.set_capacitytablecapacity;Ok())let[@inline]okx=return(Okx)let[@inline]errorx=return(Errorx)(* From RFC7541§5.1:
* decode I from the next N bits. *)letdecode_intprefixn=letmax_prefix=(1lsln)-1inleti=prefixlandmax_prefixinifi<max_prefixthenreturnielseletrecloopim=any_uint8>>=funb->leti=i+((bland127)lslm)inifbland0b1000_0000==0b1000_0000thenloopi(m+7)elsereturniinloopi0letdecode_string=any_uint8>>=funh->(* 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). *)decode_inth7>>=funstring_length->lift(funstring_data->(* From RFC7541§5.2:
* A one-bit flag, H, indicating whether or not the octets of the
* string are Huffman encoded. *)ifhland0b1000_0000==0thenOkstring_dataelseHuffman.decodestring_data)(takestring_length)letget_indexed_fieldtableindex=letstatic_table_size=Static_table.table_sizeinletdynamic_table_size=Dynamic_table.table_sizetablein(* From RFC7541§6.1:
* The index value of 0 is not used. It MUST be treated as a decoding
* error if found in an indexed header field representation. *)ifindex==0||(* From RFC7541§2.3.3:
* Indices strictly greater than the sum of the lengths of both tables
* MUST be treated as a decoding error. *)index>static_table_size+dynamic_table_sizethenErrorDecoding_errorelseifindex<=static_table_sizethen(* From RFC7541§2.3.3:
* Indices between 1 and the length of the static table (inclusive) refer
* to elements in the static table (see Section 2.3.1). *)OkStatic_table.table.(index-1)else(* From RFC7541§2.3.3:
* Indices strictly greater than the length of the static table refer to
* elements in the dynamic table (see Section 2.3.2). The length of the
* static table is subtracted to find the index into the dynamic
* table. *)Ok(Dynamic_table.gettable(index-static_table_size-1))letdecode_header_fieldtableprefixprefix_length=decode_intprefixprefix_length>>=funindex->lift2(funnamevalue->matchname,valuewith|Okname,Okvalue->Ok(name,value)|Errore,_|_,Errore->Errore)(* From RFC7541§6.2.1:
* 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,
* [...] 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 [...], followed by the
* header field name. *)(ifindex==0thendecode_stringelsematchget_indexed_fieldtableindexwith|Ok(name,_)->okname|Errore->errore)decode_stringletdecode_headers({table;_}ast)=letrecloopaccsaw_first_header=at_end_of_input>>=funis_eof->ifis_eofthenokaccelseany_uint8>>=funb->ifbland0b1000_0000!=0then(* 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). *)decode_intb7>>=funindex->matchget_indexed_fieldtableindexwith|Ok(name,value)->loop({name;value;sensitive=false}::acc)true|Errore->erroreelseifbland0b1100_0000==0b0100_0000then(* 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. In this case, the index of the
* entry is represented as an integer with a 6-bit prefix (see
* Section 5.1). *)decode_header_fieldtableb6>>=function|Ok(name,value)->(* 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. *)Dynamic_table.addtable(name,value);loop({name;value;sensitive=false}::acc)true|Errore->erroreelseifbland0b1111_0000==0then(* From RFC7541§6.2.2: Literal Header Field without Indexing
* A literal header field without indexing representation starts with
* the '0000' 4-bit pattern. In this case, the index of the entry is
* represented as an integer with a 4-bit prefix (see Section
* 5.1). *)decode_header_fieldtableb4>>=function|Ok(name,value)->loop({name;value;sensitive=false}::acc)true|Errore->erroreelseifbland0b1111_0000==0b0001_0000then(* From RFC7541§6.2.3: Literal Header Field Never Indexed
* A literal header field without indexing representation starts with
* the '0001' 4-bit pattern.
* The encoding of the representation is identical to the literal
* header field without indexing (see Section 6.2.2). *)decode_header_fieldtableb4>>=function|Ok(name,value)->loop({name;value;sensitive=true}::acc)true|Errore->erroreelseifbland0b1110_0000==0b0010_0000thenif(* From RFC7541§6.3: Dynamic Table Size Update
* A dynamic table size update signals a change to the size of the
* dynamic table.
* A dynamic table size update starts with the '001' 3-bit
* pattern *)saw_first_headerthen(* From RFC7541§4.2: Maximum Table Size
* A change in the maximum size of the dynamic table is signaled
* via a dynamic table size update (see Section 6.3). This dynamic
* table size update MUST occur at the beginning of the first
* header block following the change to the dynamic table size. *)errorDecoding_errorelsedecode_intb5>>=funcapacity->matchset_capacitytcapacitywith|Ok()->loopaccsaw_first_header|Errore->erroreelseerrorDecoding_errorinloop[]false