123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139(*----------------------------------------------------------------------------
* 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.
*---------------------------------------------------------------------------*)typet={mutableentries:(string*string*int)array;mutablelength:int;mutableoffset:int;mutablecapacity:int(* `length` above is the number of entries in the dynamic table. We
* track the HPACK size in `size`.
*
* From RFC7541§4.1:
* The size of the dynamic table is the sum of the size of its
* entries.
*
* The size of an entry is the sum of its name's length in octets (as
* defined in Section 5.2), its value's length in octets, and 32. *);mutablesize:int(* From RFC7541§4.2:
* Protocols that use HPACK determine the maximum size that the
* encoder is permitted to use for the dynamic table. In HTTP/2, this
* value is determined by the SETTINGS_HEADER_TABLE_SIZE setting (see
* Section 6.5.2 of [HTTP2]). *);mutablemax_size:int;on_evict:string*string->unit}(* From RFC7541§4.1:
* The size of an entry is the sum of its name's length in octets (as defined
* in Section 5.2), its value's length in octets, and 32. *)letdefault_entry="","",32letdefault_evict=Sys.opaque_identity(fun_->())letcreate?(on_evict=default_evict)max_size=letcapacity=max256max_sizein{entries=Array.makecapacitydefault_entry;length=0;offset=0;capacity;size=0;max_size;on_evict}let[@inline]_gettablei=table.entries.((table.offset+i)modtable.capacity)let[@inline]gettablei=letname,value,_=_gettableiinname,valuelet[@inline]entry_sizenamevalue=(* From RFC7541§4.1:
* The size of an entry is the sum of its name's length in octets (as
* defined in Section 5.2), its value's length in octets, and 32. *)String.lengthname+String.lengthvalue+32(* Note: Assumes table.size is positive. Doesn't perform any checking. *)letevict_one({capacity;entries;on_evict;_}astable)=table.length<-table.length-1;leti=(table.offset+table.length)modcapacityinletname,value,entry_size=entries.(i)inentries.(i)<-default_entry;table.size<-table.size-entry_size;(* Don't bother calling if the eviction callback is not meaningful. *)ifon_evict!=default_evictthenon_evict(name,value)letincrease_capacitytable=letnew_capacity=2*table.capacityinletnew_entries=Array.initnew_capacity(funi->ifi<table.lengththen_gettableielsedefault_entry)intable.entries<-new_entries;table.offset<-0;table.capacity<-new_capacityletadd({max_size;_}astable)(name,value)=letentry_size=entry_sizenamevaluein(* From RFC7541§4.4:
* Before a new entry is added to the dynamic table, entries are evicted
* from the end of the dynamic table until the size of the dynamic table is
* less than or equal to (maximum size - new entry size) or until the table
* is empty. *)whiletable.size>0&&table.size+entry_size>max_sizedoevict_onetabledone;(* From RFC7541§4.4:
* If the size of the new entry is less than or equal to the maximum size,
* that entry is added to the table. *)iftable.size+entry_size<=max_sizethen(iftable.length=table.capacitythenincrease_capacitytable;table.length<-table.length+1;table.size<-table.size+entry_size;letnew_offset=(table.offset+table.capacity-1)modtable.capacityintable.entries.(new_offset)<-name,value,entry_size;table.offset<-new_offset)let[@inline]table_sizetable=table.lengthletset_capacitytablemax_size=table.max_size<-max_size;(* From RFC7541§4.3:
* Whenever the maximum size for the dynamic table is reduced, entries are
* evicted from the end of the dynamic table until the size of the dynamic
* table is less than or equal to the maximum size. *)whiletable.size>max_sizedoevict_onetabledone