From adeb5bb8024d0ca020001f77872ef141dad44503 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Apr 04 2016 15:06:49 +0000 Subject: Initial package Signed-off-by: Peter Lemenkov --- diff --git a/.gitignore b/.gitignore index e69de29..8141bfa 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +/cowlib-1.3.0.tar.gz diff --git a/erlang-cowlib-0001-Add-HPACK-decoding-and-encoding-functions.patch b/erlang-cowlib-0001-Add-HPACK-decoding-and-encoding-functions.patch new file mode 100644 index 0000000..eb26cc3 --- /dev/null +++ b/erlang-cowlib-0001-Add-HPACK-decoding-and-encoding-functions.patch @@ -0,0 +1,1305 @@ +From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= +Date: Thu, 19 Mar 2015 16:07:25 +0100 +Subject: [PATCH] Add HPACK decoding and encoding functions + +No decoding options have been added in this commit. They will be +added in the future. + +Only one encoding option has been added: #{huffman => boolean()}. +It allows to disable Huffman compression of strings. This compression +is enabled by default. + +diff --git a/src/cow_hpack.erl b/src/cow_hpack.erl +new file mode 100644 +index 0000000..efabaa6 +--- /dev/null ++++ b/src/cow_hpack.erl +@@ -0,0 +1,1288 @@ ++%% Copyright (c) 2015, Loïc Hoguin ++%% ++%% Permission to use, copy, modify, and/or distribute this software for any ++%% purpose with or without fee is hereby granted, provided that the above ++%% copyright notice and this permission notice appear in all copies. ++%% ++%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++%% The current implementation is not suitable for use in ++%% intermediaries as the information about headers that ++%% should never be indexed is currently lost. ++ ++-module(cow_hpack). ++ ++-export([init/0]). ++-export([init/1]). ++ ++-export([decode/1]). ++-export([decode/2]). ++-export([decode/3]). ++ ++-export([encode/1]). ++-export([encode/2]). ++-export([encode/3]). ++ ++-record(state, { ++ size = 0 :: non_neg_integer(), ++ max_size = 4096 :: non_neg_integer(), ++ dyn_table = [] :: queue:queue({binary(), binary()}) ++}). ++ ++-opaque state() :: #state{}. ++-export_type([state/0]). ++ ++-type opts() :: map(). ++-export_type([opts/0]). ++ ++-ifdef(TEST). ++-include_lib("triq/include/triq.hrl"). ++-endif. ++ ++%% State initialization. ++ ++-spec init() -> state(). ++init() -> ++ #state{}. ++ ++-spec init(non_neg_integer()) -> state(). ++init(MaxSize) -> ++ #state{max_size=MaxSize}. ++ ++%% Decoding. ++ ++-spec decode(binary()) -> {cow_http:headers(), state()}. ++decode(Data) -> ++ decode(Data, init(), #{}). ++ ++-spec decode(binary(), State) -> {cow_http:headers(), State} when State::state(). ++decode(Data, State) -> ++ decode(Data, State, #{}). ++ ++-spec decode(binary(), State, opts()) -> {cow_http:headers(), State} when State::state(). ++decode(Data, State, Opts) -> ++ decode(Data, State, Opts, []). ++ ++decode(<<>>, State, _, Acc) -> ++ {lists:reverse(Acc), State}; ++%% Indexed header field representation. ++decode(<< 1:1, Rest/bits >>, State, Opts, Acc) -> ++ dec_indexed(Rest, State, Opts, Acc); ++%% Literal header field with incremental indexing: new name. ++decode(<< 0:1, 1:1, 0:6, Rest/bits >>, State, Opts, Acc) -> ++ dec_lit_index_new_name(Rest, State, Opts, Acc); ++%% Literal header field with incremental indexing: indexed name. ++decode(<< 0:1, 1:1, Rest/bits >>, State, Opts, Acc) -> ++ dec_lit_index_indexed_name(Rest, State, Opts, Acc); ++%% Literal header field without indexing: new name. ++decode(<< 0:8, Rest/bits >>, State, Opts, Acc) -> ++ dec_lit_no_index_new_name(Rest, State, Opts, Acc); ++%% Literal header field without indexing: indexed name. ++decode(<< 0:4, Rest/bits >>, State, Opts, Acc) -> ++ dec_lit_no_index_indexed_name(Rest, State, Opts, Acc); ++%% Literal header field never indexed: new name. ++%% @todo Keep track of "never indexed" headers. ++decode(<< 0:3, 1:1, 0:4, Rest/bits >>, State, Opts, Acc) -> ++ dec_lit_no_index_new_name(Rest, State, Opts, Acc); ++%% Literal header field never indexed: indexed name. ++%% @todo Keep track of "never indexed" headers. ++decode(<< 0:3, 1:1, Rest/bits >>, State, Opts, Acc) -> ++ dec_lit_no_index_indexed_name(Rest, State, Opts, Acc); ++%% Dynamic table size update. ++decode(<< 0:2, 1:1, Rest/bits >>, State, Opts, Acc) -> ++ dec_table_size_update(Rest, State, Opts, Acc). ++ ++%% Indexed header field representation. ++ ++dec_indexed(Rest, State, Opts, Acc) -> ++ {Index, Rest2} = dec_int7(Rest), ++ {Name, Value} = table_get(Index, State), ++ decode(Rest2, State, Opts, [{Name, Value}|Acc]). ++ ++%% Literal header field with incremental indexing. ++ ++dec_lit_index_new_name(Rest, State, Opts, Acc) -> ++ {Name, Rest2} = dec_str(Rest), ++ dec_lit_index(Rest2, State, Opts, Acc, Name). ++ ++dec_lit_index_indexed_name(Rest, State, Opts, Acc) -> ++ {Index, Rest2} = dec_int6(Rest), ++ Name = table_get_name(Index, State), ++ dec_lit_index(Rest2, State, Opts, Acc, Name). ++ ++dec_lit_index(Rest, State, Opts, Acc, Name) -> ++ {Value, Rest2} = dec_str(Rest), ++ State2 = table_insert({Name, Value}, State), ++ decode(Rest2, State2, Opts, [{Name, Value}|Acc]). ++ ++%% Literal header field without indexing. ++ ++dec_lit_no_index_new_name(Rest, State, Opts, Acc) -> ++ {Name, Rest2} = dec_str(Rest), ++ dec_lit_no_index(Rest2, State, Opts, Acc, Name). ++ ++dec_lit_no_index_indexed_name(Rest, State, Opts, Acc) -> ++ {Index, Rest2} = dec_int4(Rest), ++ Name = table_get_name(Index, State), ++ dec_lit_no_index(Rest2, State, Opts, Acc, Name). ++ ++dec_lit_no_index(Rest, State, Opts, Acc, Name) -> ++ {Value, Rest2} = dec_str(Rest), ++ decode(Rest2, State, Opts, [{Name, Value}|Acc]). ++ ++%% @todo Literal header field never indexed. ++ ++%% Dynamic table size update. ++ ++dec_table_size_update(Rest, State, Opts, Acc) -> ++ {MaxSize, Rest2} = dec_int5(Rest), ++ State2 = table_update_size(MaxSize, State), ++ decode(Rest2, State2, Opts, Acc). ++ ++%% Decode an integer. ++ ++%% The HPACK format has 4 different integer prefixes length (from 4 to 7) ++%% and each can be used to create an indefinite length integer if all bits ++%% of the prefix are set to 1. ++ ++dec_int4(<< 2#1111:4, Rest/bits >>) -> ++ dec_big_int(Rest, 15, 0); ++dec_int4(<< Int:4, Rest/bits >>) -> ++ {Int, Rest}. ++ ++dec_int5(<< 2#11111:5, Rest/bits >>) -> ++ dec_big_int(Rest, 31, 0); ++dec_int5(<< Int:5, Rest/bits >>) -> ++ {Int, Rest}. ++ ++dec_int6(<< 2#111111:6, Rest/bits >>) -> ++ dec_big_int(Rest, 63, 0); ++dec_int6(<< Int:6, Rest/bits >>) -> ++ {Int, Rest}. ++ ++dec_int7(<< 2#1111111:7, Rest/bits >>) -> ++ dec_big_int(Rest, 127, 0); ++dec_int7(<< Int:7, Rest/bits >>) -> ++ {Int, Rest}. ++ ++dec_big_int(<< 0:1, Value:7, Rest/bits >>, Int, M) -> ++ {Int + (Value bsl M), Rest}; ++dec_big_int(<< 1:1, Value:7, Rest/bits >>, Int, M) -> ++ dec_big_int(Rest, Int + (Value bsl M), M + 7). ++ ++%% Decode a string. ++ ++dec_str(<< 0:1, Rest/bits >>) -> ++ {Length, Rest2} = dec_int7(Rest), ++ << Str:Length/binary, Rest3/bits >> = Rest2, ++ {Str, Rest3}; ++dec_str(<< 1:1, Rest/bits >>) -> ++ {Length, Rest2} = dec_int7(Rest), ++ dec_huffman(Rest2, Length * 8, <<>>). ++ ++%% HPACK uses a static code table for Huffman encoded strings. ++%% It has been converted into one clause per code in the following function. ++ ++%% EOS. ++dec_huffman(Rest, 0, String) -> {String, Rest}; ++dec_huffman(<<2#1:1, Rest/bits>>, 1, String) -> {String, Rest}; ++dec_huffman(<<2#11:2, Rest/bits>>, 2, String) -> {String, Rest}; ++dec_huffman(<<2#111:3, Rest/bits>>, 3, String) -> {String, Rest}; ++dec_huffman(<<2#1111:4, Rest/bits>>, 4, String) -> {String, Rest}; ++dec_huffman(<<2#11111:5, Rest/bits>>, 5, String) -> {String, Rest}; ++dec_huffman(<<2#111111:6, Rest/bits>>, 6, String) -> {String, Rest}; ++dec_huffman(<<2#1111111:7, Rest/bits>>, 7, String) -> {String, Rest}; ++%% Static code table. ++dec_huffman(<<2#00000:5, R/bits>>, L, A) -> dec_huffman(R, L - 5, <>); ++dec_huffman(<<2#00001:5, R/bits>>, L, A) -> dec_huffman(R, L - 5, <>); ++dec_huffman(<<2#00010:5, R/bits>>, L, A) -> dec_huffman(R, L - 5, <>); ++dec_huffman(<<2#00011:5, R/bits>>, L, A) -> dec_huffman(R, L - 5, <>); ++dec_huffman(<<2#00100:5, R/bits>>, L, A) -> dec_huffman(R, L - 5, <>); ++dec_huffman(<<2#00101:5, R/bits>>, L, A) -> dec_huffman(R, L - 5, <>); ++dec_huffman(<<2#00110:5, R/bits>>, L, A) -> dec_huffman(R, L - 5, <>); ++dec_huffman(<<2#00111:5, R/bits>>, L, A) -> dec_huffman(R, L - 5, <>); ++dec_huffman(<<2#01000:5, R/bits>>, L, A) -> dec_huffman(R, L - 5, <>); ++dec_huffman(<<2#01001:5, R/bits>>, L, A) -> dec_huffman(R, L - 5, <>); ++dec_huffman(<<2#010100:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#010101:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#010110:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#010111:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#011000:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#011001:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#011010:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#011011:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#011100:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#011101:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#011110:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#011111:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#100000:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#100001:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#100010:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#100011:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#100100:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#100101:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#100110:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#100111:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#101000:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#101001:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#101010:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#101011:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#101100:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#101101:6, R/bits>>, L, A) -> dec_huffman(R, L - 6, <>); ++dec_huffman(<<2#1011100:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1011101:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1011110:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1011111:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1100000:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1100001:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1100010:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1100011:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1100100:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1100101:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1100110:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1100111:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1101000:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1101001:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1101010:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1101011:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1101100:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1101101:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1101110:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1101111:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1110000:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1110001:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1110010:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1110011:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1110100:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1110101:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1110110:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1110111:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1111000:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1111001:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1111010:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#1111011:7, R/bits>>, L, A) -> dec_huffman(R, L - 7, <>); ++dec_huffman(<<2#11111000:8, R/bits>>, L, A) -> dec_huffman(R, L - 8, <>); ++dec_huffman(<<2#11111001:8, R/bits>>, L, A) -> dec_huffman(R, L - 8, <>); ++dec_huffman(<<2#11111010:8, R/bits>>, L, A) -> dec_huffman(R, L - 8, <>); ++dec_huffman(<<2#11111011:8, R/bits>>, L, A) -> dec_huffman(R, L - 8, <>); ++dec_huffman(<<2#11111100:8, R/bits>>, L, A) -> dec_huffman(R, L - 8, <>); ++dec_huffman(<<2#11111101:8, R/bits>>, L, A) -> dec_huffman(R, L - 8, <>); ++dec_huffman(<<2#1111111000:10, R/bits>>, L, A) -> dec_huffman(R, L - 10, <>); ++dec_huffman(<<2#1111111001:10, R/bits>>, L, A) -> dec_huffman(R, L - 10, <>); ++dec_huffman(<<2#1111111010:10, R/bits>>, L, A) -> dec_huffman(R, L - 10, <>); ++dec_huffman(<<2#1111111011:10, R/bits>>, L, A) -> dec_huffman(R, L - 10, <>); ++dec_huffman(<<2#1111111100:10, R/bits>>, L, A) -> dec_huffman(R, L - 10, <>); ++dec_huffman(<<2#11111111010:11, R/bits>>, L, A) -> dec_huffman(R, L - 11, <>); ++dec_huffman(<<2#11111111011:11, R/bits>>, L, A) -> dec_huffman(R, L - 11, <>); ++dec_huffman(<<2#11111111100:11, R/bits>>, L, A) -> dec_huffman(R, L - 11, <>); ++dec_huffman(<<2#111111111010:12, R/bits>>, L, A) -> dec_huffman(R, L - 12, <>); ++dec_huffman(<<2#111111111011:12, R/bits>>, L, A) -> dec_huffman(R, L - 12, <>); ++dec_huffman(<<2#1111111111000:13, R/bits>>, L, A) -> dec_huffman(R, L - 13, <>); ++dec_huffman(<<2#1111111111001:13, R/bits>>, L, A) -> dec_huffman(R, L - 13, <>); ++dec_huffman(<<2#1111111111010:13, R/bits>>, L, A) -> dec_huffman(R, L - 13, <>); ++dec_huffman(<<2#1111111111011:13, R/bits>>, L, A) -> dec_huffman(R, L - 13, <>); ++dec_huffman(<<2#1111111111100:13, R/bits>>, L, A) -> dec_huffman(R, L - 13, <>); ++dec_huffman(<<2#1111111111101:13, R/bits>>, L, A) -> dec_huffman(R, L - 13, <>); ++dec_huffman(<<2#11111111111100:14, R/bits>>, L, A) -> dec_huffman(R, L - 14, <>); ++dec_huffman(<<2#11111111111101:14, R/bits>>, L, A) -> dec_huffman(R, L - 14, <>); ++dec_huffman(<<2#111111111111100:15, R/bits>>, L, A) -> dec_huffman(R, L - 15, <>); ++dec_huffman(<<2#111111111111101:15, R/bits>>, L, A) -> dec_huffman(R, L - 15, <>); ++dec_huffman(<<2#111111111111110:15, R/bits>>, L, A) -> dec_huffman(R, L - 15, <>); ++dec_huffman(<<2#1111111111111110000:19, R/bits>>, L, A) -> dec_huffman(R, L - 19, <>); ++dec_huffman(<<2#1111111111111110001:19, R/bits>>, L, A) -> dec_huffman(R, L - 19, <>); ++dec_huffman(<<2#1111111111111110010:19, R/bits>>, L, A) -> dec_huffman(R, L - 19, <>); ++dec_huffman(<<2#11111111111111100110:20, R/bits>>, L, A) -> dec_huffman(R, L - 20, <>); ++dec_huffman(<<2#11111111111111100111:20, R/bits>>, L, A) -> dec_huffman(R, L - 20, <>); ++dec_huffman(<<2#11111111111111101000:20, R/bits>>, L, A) -> dec_huffman(R, L - 20, <>); ++dec_huffman(<<2#11111111111111101001:20, R/bits>>, L, A) -> dec_huffman(R, L - 20, <>); ++dec_huffman(<<2#11111111111111101010:20, R/bits>>, L, A) -> dec_huffman(R, L - 20, <>); ++dec_huffman(<<2#11111111111111101011:20, R/bits>>, L, A) -> dec_huffman(R, L - 20, <>); ++dec_huffman(<<2#11111111111111101100:20, R/bits>>, L, A) -> dec_huffman(R, L - 20, <>); ++dec_huffman(<<2#11111111111111101101:20, R/bits>>, L, A) -> dec_huffman(R, L - 20, <>); ++dec_huffman(<<2#111111111111111011100:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111011101:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111011110:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111011111:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111100000:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111100001:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111100010:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111100011:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111100100:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111100101:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111100110:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111100111:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#111111111111111101000:21, R/bits>>, L, A) -> dec_huffman(R, L - 21, <>); ++dec_huffman(<<2#1111111111111111010010:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111010011:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111010100:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111010101:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111010110:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111010111:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111011000:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111011001:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111011010:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111011011:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111011100:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111011101:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111011110:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111011111:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111100000:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111100001:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111100010:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111100011:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111100100:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111100101:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111100110:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111100111:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111101000:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111101001:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111101010:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#1111111111111111101011:22, R/bits>>, L, A) -> dec_huffman(R, L - 22, <>); ++dec_huffman(<<2#11111111111111111011000:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111011001:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111011010:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111011011:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111011100:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111011101:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111011110:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111011111:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111100000:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111100001:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111100010:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111100011:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111100100:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111100101:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111100110:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111100111:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111101000:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111101001:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111101010:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111101011:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111101100:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111101101:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111101110:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111101111:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111110000:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111110001:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111110010:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111110011:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#11111111111111111110100:23, R/bits>>, L, A) -> dec_huffman(R, L - 23, <>); ++dec_huffman(<<2#111111111111111111101010:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#111111111111111111101011:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#111111111111111111101100:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#111111111111111111101101:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#111111111111111111101110:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#111111111111111111101111:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#111111111111111111110000:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#111111111111111111110001:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#111111111111111111110010:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#111111111111111111110011:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#111111111111111111110100:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#111111111111111111110101:24, R/bits>>, L, A) -> dec_huffman(R, L - 24, <>); ++dec_huffman(<<2#1111111111111111111101100:25, R/bits>>, L, A) -> dec_huffman(R, L - 25, <>); ++dec_huffman(<<2#1111111111111111111101101:25, R/bits>>, L, A) -> dec_huffman(R, L - 25, <>); ++dec_huffman(<<2#1111111111111111111101110:25, R/bits>>, L, A) -> dec_huffman(R, L - 25, <>); ++dec_huffman(<<2#1111111111111111111101111:25, R/bits>>, L, A) -> dec_huffman(R, L - 25, <>); ++dec_huffman(<<2#11111111111111111111100000:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111100001:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111100010:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111100011:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111100100:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111100101:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111100110:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111100111:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111101000:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111101001:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111101010:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111101011:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111101100:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111101101:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#11111111111111111111101110:26, R/bits>>, L, A) -> dec_huffman(R, L - 26, <>); ++dec_huffman(<<2#111111111111111111111011110:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111011111:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111100000:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111100001:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111100010:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111100011:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111100100:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111100101:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111100110:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111100111:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111101000:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111101001:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111101010:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111101011:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111101100:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111101101:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111101110:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111101111:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#111111111111111111111110000:27, R/bits>>, L, A) -> dec_huffman(R, L - 27, <>); ++dec_huffman(<<2#1111111111111111111111100010:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111100011:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111100100:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111100101:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111100110:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111100111:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111101000:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111101001:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111101010:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111101011:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111101100:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111101101:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111101110:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111101111:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111110000:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111110001:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111110010:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111110011:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111110100:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111110101:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111110110:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111110111:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111111000:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111111001:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111111010:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111111011:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111111100:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111111101:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#1111111111111111111111111110:28, R/bits>>, L, A) -> dec_huffman(R, L - 28, <>); ++dec_huffman(<<2#111111111111111111111111111100:30, R/bits>>, L, A) -> dec_huffman(R, L - 30, <>); ++dec_huffman(<<2#111111111111111111111111111101:30, R/bits>>, L, A) -> dec_huffman(R, L - 30, <>); ++dec_huffman(<<2#111111111111111111111111111110:30, R/bits>>, L, A) -> dec_huffman(R, L - 30, <>). ++ ++-ifdef(TEST). ++req_decode_test() -> ++ %% First request (raw then huffman). ++ {Headers1, State1} = decode(<< 16#828684410f7777772e6578616d706c652e636f6d:160 >>), ++ {Headers1, State1} = decode(<< 16#828684418cf1e3c2e5f23a6ba0ab90f4ff:136 >>), ++ Headers1 = [ ++ {<<":method">>, <<"GET">>}, ++ {<<":scheme">>, <<"http">>}, ++ {<<":path">>, <<"/">>}, ++ {<<":authority">>, <<"www.example.com">>} ++ ], ++ #state{size=57, dyn_table=[{57,{<<":authority">>, <<"www.example.com">>}}]} = State1, ++ %% Second request (raw then huffman). ++ {Headers2, State2} = decode(<< 16#828684be58086e6f2d6361636865:112 >>, State1), ++ {Headers2, State2} = decode(<< 16#828684be5886a8eb10649cbf:96 >>, State1), ++ Headers2 = [ ++ {<<":method">>, <<"GET">>}, ++ {<<":scheme">>, <<"http">>}, ++ {<<":path">>, <<"/">>}, ++ {<<":authority">>, <<"www.example.com">>}, ++ {<<"cache-control">>, <<"no-cache">>} ++ ], ++ #state{size=110, dyn_table=[ ++ {53,{<<"cache-control">>, <<"no-cache">>}}, ++ {57,{<<":authority">>, <<"www.example.com">>}}]} = State2, ++ %% Third request (raw then huffman). ++ {Headers3, State3} = decode(<< 16#828785bf400a637573746f6d2d6b65790c637573746f6d2d76616c7565:232 >>, State2), ++ {Headers3, State3} = decode(<< 16#828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf:192 >>, State2), ++ Headers3 = [ ++ {<<":method">>, <<"GET">>}, ++ {<<":scheme">>, <<"https">>}, ++ {<<":path">>, <<"/index.html">>}, ++ {<<":authority">>, <<"www.example.com">>}, ++ {<<"custom-key">>, <<"custom-value">>} ++ ], ++ #state{size=164, dyn_table=[ ++ {54,{<<"custom-key">>, <<"custom-value">>}}, ++ {53,{<<"cache-control">>, <<"no-cache">>}}, ++ {57,{<<":authority">>, <<"www.example.com">>}}]} = State3, ++ ok. ++ ++resp_decode_test() -> ++ %% Use a max_size of 256 to trigger header evictions. ++ State0 = init(256), ++ %% First response (raw then huffman). ++ {Headers1, State1} = decode(<< 16#4803333032580770726976617465611d4d6f6e2c203231204f637420323031332032303a31333a323120474d546e1768747470733a2f2f7777772e6578616d706c652e636f6d:560 >>, State0), ++ {Headers1, State1} = decode(<< 16#488264025885aec3771a4b6196d07abe941054d444a8200595040b8166e082a62d1bff6e919d29ad171863c78f0b97c8e9ae82ae43d3:432 >>, State0), ++ Headers1 = [ ++ {<<":status">>, <<"302">>}, ++ {<<"cache-control">>, <<"private">>}, ++ {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}, ++ {<<"location">>, <<"https://www.example.com">>} ++ ], ++ #state{size=222, dyn_table=[ ++ {63,{<<"location">>, <<"https://www.example.com">>}}, ++ {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}}, ++ {52,{<<"cache-control">>, <<"private">>}}, ++ {42,{<<":status">>, <<"302">>}}]} = State1, ++ %% Second response (raw then huffman). ++ {Headers2, State2} = decode(<< 16#4803333037c1c0bf:64 >>, State1), ++ {Headers2, State2} = decode(<< 16#4883640effc1c0bf:64 >>, State1), ++ Headers2 = [ ++ {<<":status">>, <<"307">>}, ++ {<<"cache-control">>, <<"private">>}, ++ {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}, ++ {<<"location">>, <<"https://www.example.com">>} ++ ], ++ #state{size=180, dyn_table=[ ++ {42,{<<":status">>, <<"307">>}}, ++ {63,{<<"location">>, <<"https://www.example.com">>}}, ++ {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}}, ++ {52,{<<"cache-control">>, <<"private">>}}]} = State2, ++ %% Third response (raw then huffman). ++ {Headers3, State3} = decode(<< 16#88c1611d4d6f6e2c203231204f637420323031332032303a31333a323220474d54c05a04677a69707738666f6f3d4153444a4b48514b425a584f5157454f50495541585157454f49553b206d61782d6167653d333630303b2076657273696f6e3d31:784 >>, State2), ++ {Headers3, State3} = decode(<< 16#88c16196d07abe941054d444a8200595040b8166e084a62d1bffc05a839bd9ab77ad94e7821dd7f2e6c7b335dfdfcd5b3960d5af27087f3672c1ab270fb5291f9587316065c003ed4ee5b1063d5007:632 >>, State2), ++ Headers3 = [ ++ {<<":status">>, <<"200">>}, ++ {<<"cache-control">>, <<"private">>}, ++ {<<"date">>, <<"Mon, 21 Oct 2013 20:13:22 GMT">>}, ++ {<<"location">>, <<"https://www.example.com">>}, ++ {<<"content-encoding">>, <<"gzip">>}, ++ {<<"set-cookie">>, <<"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1">>} ++ ], ++ #state{size=117, dyn_table=[ ++ {98,{<<"set-cookie">>, <<"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1">>}}, ++ {52,{<<"content-encoding">>, <<"gzip">>}}, ++ {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:22 GMT">>}}]} = State3, ++ ok. ++-endif. ++ ++%% Encoding. ++ ++-spec encode(cow_http:headers()) -> iodata(). ++encode(Headers) -> ++ encode(Headers, init(), #{}, []). ++ ++-spec encode(cow_http:headers(), State) -> iodata() when State::state(). ++encode(Headers, State) -> ++ encode(Headers, State, #{}, []). ++ ++-spec encode(cow_http:headers(), State, opts()) -> iodata() when State::state(). ++encode(Headers, State, Opts) -> ++ encode(Headers, State, Opts, []). ++ ++%% @todo Handle cases where no/never indexing is expected. ++encode([], State, _, Acc) -> ++ {lists:reverse(Acc), State}; ++encode([Header = {Name, Value}|Tail], State, Opts, Acc) -> ++ case table_find(Header, State) of ++ %% Indexed header field representation. ++ {field, Index} -> ++ encode(Tail, State, Opts, [enc_int7(Index, 2#1)|Acc]); ++ %% Literal header field representation: indexed name. ++ {name, Index} -> ++ State2 = table_insert(Header, State), ++ encode(Tail, State2, Opts, [[enc_int6(Index, 2#01), enc_str(Value, Opts)]|Acc]); ++ %% Literal header field representation: new name. ++ not_found -> ++ State2 = table_insert(Header, State), ++ encode(Tail, State2, Opts, [[<< 0:1, 1:1, 0:6 >>, enc_str(Name, Opts), enc_str(Value, Opts)]|Acc]) ++ end. ++ ++%% Encode an integer. ++ ++enc_int6(Int, Prefix) when Int < 63 -> ++ << Prefix:2, Int:6 >>; ++enc_int6(Int, Prefix) -> ++ [<< Prefix:2, 2#111111:6 >>|enc_big_int(Int - 63, [])]. ++ ++enc_int7(Int, Prefix) when Int < 127 -> ++ << Prefix:1, Int:7 >>; ++enc_int7(Int, Prefix) -> ++ [<< Prefix:1, 2#1111111:7 >>|enc_big_int(Int - 127, [])]. ++ ++enc_big_int(Int, Acc) when Int < 128 -> ++ lists:reverse([<< Int:8 >>|Acc]); ++enc_big_int(Int, Acc) -> ++ enc_big_int(Int bsr 7, [<< 1:1, Int:7 >>|Acc]). ++ ++%% Encode a string. ++ ++enc_str(Str, Opts) -> ++ case maps:get(huffman, Opts, true) of ++ true -> ++ Str2 = enc_huffman(Str, <<>>), ++ [enc_int7(byte_size(Str2), 2#1), Str2]; ++ false -> ++ [enc_int7(iolist_size(Str), 2#0), Str] ++ end. ++ ++enc_huffman(<<>>, Acc) -> ++ case bit_size(Acc) rem 8 of ++ 1 -> << Acc/bits, 2#1111111:7 >>; ++ 2 -> << Acc/bits, 2#111111:6 >>; ++ 3 -> << Acc/bits, 2#11111:5 >>; ++ 4 -> << Acc/bits, 2#1111:4 >>; ++ 5 -> << Acc/bits, 2#111:3 >>; ++ 6 -> << Acc/bits, 2#11:2 >>; ++ 7 -> << Acc/bits, 2#1:1 >>; ++ 0 -> Acc ++ end; ++enc_huffman(<< 0, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111000:13 >>); ++enc_huffman(<< 1, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111011000:23 >>); ++enc_huffman(<< 2, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111100010:28 >>); ++enc_huffman(<< 3, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111100011:28 >>); ++enc_huffman(<< 4, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111100100:28 >>); ++enc_huffman(<< 5, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111100101:28 >>); ++enc_huffman(<< 6, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111100110:28 >>); ++enc_huffman(<< 7, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111100111:28 >>); ++enc_huffman(<< 8, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111101000:28 >>); ++enc_huffman(<< 9, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111101010:24 >>); ++enc_huffman(<< 10, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111111111100:30 >>); ++enc_huffman(<< 11, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111101001:28 >>); ++enc_huffman(<< 12, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111101010:28 >>); ++enc_huffman(<< 13, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111111111101:30 >>); ++enc_huffman(<< 14, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111101011:28 >>); ++enc_huffman(<< 15, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111101100:28 >>); ++enc_huffman(<< 16, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111101101:28 >>); ++enc_huffman(<< 17, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111101110:28 >>); ++enc_huffman(<< 18, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111101111:28 >>); ++enc_huffman(<< 19, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111110000:28 >>); ++enc_huffman(<< 20, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111110001:28 >>); ++enc_huffman(<< 21, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111110010:28 >>); ++enc_huffman(<< 22, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111111111110:30 >>); ++enc_huffman(<< 23, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111110011:28 >>); ++enc_huffman(<< 24, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111110100:28 >>); ++enc_huffman(<< 25, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111110101:28 >>); ++enc_huffman(<< 26, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111110110:28 >>); ++enc_huffman(<< 27, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111110111:28 >>); ++enc_huffman(<< 28, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111111000:28 >>); ++enc_huffman(<< 29, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111111001:28 >>); ++enc_huffman(<< 30, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111111010:28 >>); ++enc_huffman(<< 31, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111111011:28 >>); ++enc_huffman(<< 32, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#010100:6 >>); ++enc_huffman(<< 33, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111000:10 >>); ++enc_huffman(<< 34, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111001:10 >>); ++enc_huffman(<< 35, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111010:12 >>); ++enc_huffman(<< 36, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111001:13 >>); ++enc_huffman(<< 37, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#010101:6 >>); ++enc_huffman(<< 38, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111000:8 >>); ++enc_huffman(<< 39, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111010:11 >>); ++enc_huffman(<< 40, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111010:10 >>); ++enc_huffman(<< 41, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111011:10 >>); ++enc_huffman(<< 42, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111001:8 >>); ++enc_huffman(<< 43, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111011:11 >>); ++enc_huffman(<< 44, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111010:8 >>); ++enc_huffman(<< 45, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#010110:6 >>); ++enc_huffman(<< 46, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#010111:6 >>); ++enc_huffman(<< 47, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#011000:6 >>); ++enc_huffman(<< 48, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#00000:5 >>); ++enc_huffman(<< 49, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#00001:5 >>); ++enc_huffman(<< 50, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#00010:5 >>); ++enc_huffman(<< 51, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#011001:6 >>); ++enc_huffman(<< 52, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#011010:6 >>); ++enc_huffman(<< 53, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#011011:6 >>); ++enc_huffman(<< 54, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#011100:6 >>); ++enc_huffman(<< 55, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#011101:6 >>); ++enc_huffman(<< 56, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#011110:6 >>); ++enc_huffman(<< 57, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#011111:6 >>); ++enc_huffman(<< 58, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1011100:7 >>); ++enc_huffman(<< 59, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111011:8 >>); ++enc_huffman(<< 60, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111100:15 >>); ++enc_huffman(<< 61, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#100000:6 >>); ++enc_huffman(<< 62, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111011:12 >>); ++enc_huffman(<< 63, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111100:10 >>); ++enc_huffman(<< 64, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111010:13 >>); ++enc_huffman(<< 65, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#100001:6 >>); ++enc_huffman(<< 66, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1011101:7 >>); ++enc_huffman(<< 67, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1011110:7 >>); ++enc_huffman(<< 68, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1011111:7 >>); ++enc_huffman(<< 69, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1100000:7 >>); ++enc_huffman(<< 70, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1100001:7 >>); ++enc_huffman(<< 71, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1100010:7 >>); ++enc_huffman(<< 72, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1100011:7 >>); ++enc_huffman(<< 73, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1100100:7 >>); ++enc_huffman(<< 74, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1100101:7 >>); ++enc_huffman(<< 75, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1100110:7 >>); ++enc_huffman(<< 76, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1100111:7 >>); ++enc_huffman(<< 77, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1101000:7 >>); ++enc_huffman(<< 78, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1101001:7 >>); ++enc_huffman(<< 79, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1101010:7 >>); ++enc_huffman(<< 80, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1101011:7 >>); ++enc_huffman(<< 81, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1101100:7 >>); ++enc_huffman(<< 82, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1101101:7 >>); ++enc_huffman(<< 83, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1101110:7 >>); ++enc_huffman(<< 84, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1101111:7 >>); ++enc_huffman(<< 85, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1110000:7 >>); ++enc_huffman(<< 86, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1110001:7 >>); ++enc_huffman(<< 87, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1110010:7 >>); ++enc_huffman(<< 88, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111100:8 >>); ++enc_huffman(<< 89, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1110011:7 >>); ++enc_huffman(<< 90, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111101:8 >>); ++enc_huffman(<< 91, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111011:13 >>); ++enc_huffman(<< 92, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111110000:19 >>); ++enc_huffman(<< 93, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111100:13 >>); ++enc_huffman(<< 94, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111100:14 >>); ++enc_huffman(<< 95, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#100010:6 >>); ++enc_huffman(<< 96, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111101:15 >>); ++enc_huffman(<< 97, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#00011:5 >>); ++enc_huffman(<< 98, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#100011:6 >>); ++enc_huffman(<< 99, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#00100:5 >>); ++enc_huffman(<< 100, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#100100:6 >>); ++enc_huffman(<< 101, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#00101:5 >>); ++enc_huffman(<< 102, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#100101:6 >>); ++enc_huffman(<< 103, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#100110:6 >>); ++enc_huffman(<< 104, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#100111:6 >>); ++enc_huffman(<< 105, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#00110:5 >>); ++enc_huffman(<< 106, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1110100:7 >>); ++enc_huffman(<< 107, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1110101:7 >>); ++enc_huffman(<< 108, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#101000:6 >>); ++enc_huffman(<< 109, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#101001:6 >>); ++enc_huffman(<< 110, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#101010:6 >>); ++enc_huffman(<< 111, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#00111:5 >>); ++enc_huffman(<< 112, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#101011:6 >>); ++enc_huffman(<< 113, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1110110:7 >>); ++enc_huffman(<< 114, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#101100:6 >>); ++enc_huffman(<< 115, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#01000:5 >>); ++enc_huffman(<< 116, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#01001:5 >>); ++enc_huffman(<< 117, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#101101:6 >>); ++enc_huffman(<< 118, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1110111:7 >>); ++enc_huffman(<< 119, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111000:7 >>); ++enc_huffman(<< 120, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111001:7 >>); ++enc_huffman(<< 121, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111010:7 >>); ++enc_huffman(<< 122, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111011:7 >>); ++enc_huffman(<< 123, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111110:15 >>); ++enc_huffman(<< 124, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111100:11 >>); ++enc_huffman(<< 125, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111101:14 >>); ++enc_huffman(<< 126, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111101:13 >>); ++enc_huffman(<< 127, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111111100:28 >>); ++enc_huffman(<< 128, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111100110:20 >>); ++enc_huffman(<< 129, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111010010:22 >>); ++enc_huffman(<< 130, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111100111:20 >>); ++enc_huffman(<< 131, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111101000:20 >>); ++enc_huffman(<< 132, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111010011:22 >>); ++enc_huffman(<< 133, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111010100:22 >>); ++enc_huffman(<< 134, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111010101:22 >>); ++enc_huffman(<< 135, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111011001:23 >>); ++enc_huffman(<< 136, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111010110:22 >>); ++enc_huffman(<< 137, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111011010:23 >>); ++enc_huffman(<< 138, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111011011:23 >>); ++enc_huffman(<< 139, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111011100:23 >>); ++enc_huffman(<< 140, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111011101:23 >>); ++enc_huffman(<< 141, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111011110:23 >>); ++enc_huffman(<< 142, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111101011:24 >>); ++enc_huffman(<< 143, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111011111:23 >>); ++enc_huffman(<< 144, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111101100:24 >>); ++enc_huffman(<< 145, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111101101:24 >>); ++enc_huffman(<< 146, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111010111:22 >>); ++enc_huffman(<< 147, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111100000:23 >>); ++enc_huffman(<< 148, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111101110:24 >>); ++enc_huffman(<< 149, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111100001:23 >>); ++enc_huffman(<< 150, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111100010:23 >>); ++enc_huffman(<< 151, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111100011:23 >>); ++enc_huffman(<< 152, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111100100:23 >>); ++enc_huffman(<< 153, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111011100:21 >>); ++enc_huffman(<< 154, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111011000:22 >>); ++enc_huffman(<< 155, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111100101:23 >>); ++enc_huffman(<< 156, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111011001:22 >>); ++enc_huffman(<< 157, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111100110:23 >>); ++enc_huffman(<< 158, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111100111:23 >>); ++enc_huffman(<< 159, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111101111:24 >>); ++enc_huffman(<< 160, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111011010:22 >>); ++enc_huffman(<< 161, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111011101:21 >>); ++enc_huffman(<< 162, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111101001:20 >>); ++enc_huffman(<< 163, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111011011:22 >>); ++enc_huffman(<< 164, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111011100:22 >>); ++enc_huffman(<< 165, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111101000:23 >>); ++enc_huffman(<< 166, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111101001:23 >>); ++enc_huffman(<< 167, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111011110:21 >>); ++enc_huffman(<< 168, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111101010:23 >>); ++enc_huffman(<< 169, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111011101:22 >>); ++enc_huffman(<< 170, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111011110:22 >>); ++enc_huffman(<< 171, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111110000:24 >>); ++enc_huffman(<< 172, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111011111:21 >>); ++enc_huffman(<< 173, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111011111:22 >>); ++enc_huffman(<< 174, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111101011:23 >>); ++enc_huffman(<< 175, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111101100:23 >>); ++enc_huffman(<< 176, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111100000:21 >>); ++enc_huffman(<< 177, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111100001:21 >>); ++enc_huffman(<< 178, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111100000:22 >>); ++enc_huffman(<< 179, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111100010:21 >>); ++enc_huffman(<< 180, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111101101:23 >>); ++enc_huffman(<< 181, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111100001:22 >>); ++enc_huffman(<< 182, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111101110:23 >>); ++enc_huffman(<< 183, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111101111:23 >>); ++enc_huffman(<< 184, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111101010:20 >>); ++enc_huffman(<< 185, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111100010:22 >>); ++enc_huffman(<< 186, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111100011:22 >>); ++enc_huffman(<< 187, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111100100:22 >>); ++enc_huffman(<< 188, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111110000:23 >>); ++enc_huffman(<< 189, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111100101:22 >>); ++enc_huffman(<< 190, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111100110:22 >>); ++enc_huffman(<< 191, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111110001:23 >>); ++enc_huffman(<< 192, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111100000:26 >>); ++enc_huffman(<< 193, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111100001:26 >>); ++enc_huffman(<< 194, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111101011:20 >>); ++enc_huffman(<< 195, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111110001:19 >>); ++enc_huffman(<< 196, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111100111:22 >>); ++enc_huffman(<< 197, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111110010:23 >>); ++enc_huffman(<< 198, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111101000:22 >>); ++enc_huffman(<< 199, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111101100:25 >>); ++enc_huffman(<< 200, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111100010:26 >>); ++enc_huffman(<< 201, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111100011:26 >>); ++enc_huffman(<< 202, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111100100:26 >>); ++enc_huffman(<< 203, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111011110:27 >>); ++enc_huffman(<< 204, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111011111:27 >>); ++enc_huffman(<< 205, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111100101:26 >>); ++enc_huffman(<< 206, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111110001:24 >>); ++enc_huffman(<< 207, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111101101:25 >>); ++enc_huffman(<< 208, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111110010:19 >>); ++enc_huffman(<< 209, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111100011:21 >>); ++enc_huffman(<< 210, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111100110:26 >>); ++enc_huffman(<< 211, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111100000:27 >>); ++enc_huffman(<< 212, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111100001:27 >>); ++enc_huffman(<< 213, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111100111:26 >>); ++enc_huffman(<< 214, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111100010:27 >>); ++enc_huffman(<< 215, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111110010:24 >>); ++enc_huffman(<< 216, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111100100:21 >>); ++enc_huffman(<< 217, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111100101:21 >>); ++enc_huffman(<< 218, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111101000:26 >>); ++enc_huffman(<< 219, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111101001:26 >>); ++enc_huffman(<< 220, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111111101:28 >>); ++enc_huffman(<< 221, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111100011:27 >>); ++enc_huffman(<< 222, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111100100:27 >>); ++enc_huffman(<< 223, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111100101:27 >>); ++enc_huffman(<< 224, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111101100:20 >>); ++enc_huffman(<< 225, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111110011:24 >>); ++enc_huffman(<< 226, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111101101:20 >>); ++enc_huffman(<< 227, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111100110:21 >>); ++enc_huffman(<< 228, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111101001:22 >>); ++enc_huffman(<< 229, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111100111:21 >>); ++enc_huffman(<< 230, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111101000:21 >>); ++enc_huffman(<< 231, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111110011:23 >>); ++enc_huffman(<< 232, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111101010:22 >>); ++enc_huffman(<< 233, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111101011:22 >>); ++enc_huffman(<< 234, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111101110:25 >>); ++enc_huffman(<< 235, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111101111:25 >>); ++enc_huffman(<< 236, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111110100:24 >>); ++enc_huffman(<< 237, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111110101:24 >>); ++enc_huffman(<< 238, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111101010:26 >>); ++enc_huffman(<< 239, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111110100:23 >>); ++enc_huffman(<< 240, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111101011:26 >>); ++enc_huffman(<< 241, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111100110:27 >>); ++enc_huffman(<< 242, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111101100:26 >>); ++enc_huffman(<< 243, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111101101:26 >>); ++enc_huffman(<< 244, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111100111:27 >>); ++enc_huffman(<< 245, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111101000:27 >>); ++enc_huffman(<< 246, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111101001:27 >>); ++enc_huffman(<< 247, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111101010:27 >>); ++enc_huffman(<< 248, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111101011:27 >>); ++enc_huffman(<< 249, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#1111111111111111111111111110:28 >>); ++enc_huffman(<< 250, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111101100:27 >>); ++enc_huffman(<< 251, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111101101:27 >>); ++enc_huffman(<< 252, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111101110:27 >>); ++enc_huffman(<< 253, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111101111:27 >>); ++enc_huffman(<< 254, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#111111111111111111111110000:27 >>); ++enc_huffman(<< 255, R/bits >>, A) -> enc_huffman(R, << A/bits, 2#11111111111111111111101110:26 >>). ++ ++-ifdef(TEST). ++req_encode_test() -> ++ %% First request (raw then huffman). ++ Headers1 = [ ++ {<<":method">>, <<"GET">>}, ++ {<<":scheme">>, <<"http">>}, ++ {<<":path">>, <<"/">>}, ++ {<<":authority">>, <<"www.example.com">>} ++ ], ++ {Raw1, State1} = encode(Headers1, init(), #{huffman => false}), ++ << 16#828684410f7777772e6578616d706c652e636f6d:160 >> = iolist_to_binary(Raw1), ++ {Huff1, State1} = encode(Headers1), ++ << 16#828684418cf1e3c2e5f23a6ba0ab90f4ff:136 >> = iolist_to_binary(Huff1), ++ #state{size=57, dyn_table=[{57,{<<":authority">>, <<"www.example.com">>}}]} = State1, ++ %% Second request (raw then huffman). ++ Headers2 = [ ++ {<<":method">>, <<"GET">>}, ++ {<<":scheme">>, <<"http">>}, ++ {<<":path">>, <<"/">>}, ++ {<<":authority">>, <<"www.example.com">>}, ++ {<<"cache-control">>, <<"no-cache">>} ++ ], ++ {Raw2, State2} = encode(Headers2, State1, #{huffman => false}), ++ << 16#828684be58086e6f2d6361636865:112 >> = iolist_to_binary(Raw2), ++ {Huff2, State2} = encode(Headers2, State1), ++ << 16#828684be5886a8eb10649cbf:96 >> = iolist_to_binary(Huff2), ++ #state{size=110, dyn_table=[ ++ {53,{<<"cache-control">>, <<"no-cache">>}}, ++ {57,{<<":authority">>, <<"www.example.com">>}}]} = State2, ++ %% Third request (raw then huffman). ++ Headers3 = [ ++ {<<":method">>, <<"GET">>}, ++ {<<":scheme">>, <<"https">>}, ++ {<<":path">>, <<"/index.html">>}, ++ {<<":authority">>, <<"www.example.com">>}, ++ {<<"custom-key">>, <<"custom-value">>} ++ ], ++ {Raw3, State3} = encode(Headers3, State2, #{huffman => false}), ++ << 16#828785bf400a637573746f6d2d6b65790c637573746f6d2d76616c7565:232 >> = iolist_to_binary(Raw3), ++ {Huff3, State3} = encode(Headers3, State2), ++ << 16#828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf:192 >> = iolist_to_binary(Huff3), ++ #state{size=164, dyn_table=[ ++ {54,{<<"custom-key">>, <<"custom-value">>}}, ++ {53,{<<"cache-control">>, <<"no-cache">>}}, ++ {57,{<<":authority">>, <<"www.example.com">>}}]} = State3, ++ ok. ++ ++resp_encode_test() -> ++ %% Use a max_size of 256 to trigger header evictions. ++ State0 = init(256), ++ %% First response (raw then huffman). ++ Headers1 = [ ++ {<<":status">>, <<"302">>}, ++ {<<"cache-control">>, <<"private">>}, ++ {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}, ++ {<<"location">>, <<"https://www.example.com">>} ++ ], ++ {Raw1, State1} = encode(Headers1, State0, #{huffman => false}), ++ << 16#4803333032580770726976617465611d4d6f6e2c203231204f637420323031332032303a31333a323120474d546e1768747470733a2f2f7777772e6578616d706c652e636f6d:560 >> = iolist_to_binary(Raw1), ++ {Huff1, State1} = encode(Headers1, State0), ++ << 16#488264025885aec3771a4b6196d07abe941054d444a8200595040b8166e082a62d1bff6e919d29ad171863c78f0b97c8e9ae82ae43d3:432 >> = iolist_to_binary(Huff1), ++ #state{size=222, dyn_table=[ ++ {63,{<<"location">>, <<"https://www.example.com">>}}, ++ {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}}, ++ {52,{<<"cache-control">>, <<"private">>}}, ++ {42,{<<":status">>, <<"302">>}}]} = State1, ++ %% Second response (raw then huffman). ++ Headers2 = [ ++ {<<":status">>, <<"307">>}, ++ {<<"cache-control">>, <<"private">>}, ++ {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}, ++ {<<"location">>, <<"https://www.example.com">>} ++ ], ++ {Raw2, State2} = encode(Headers2, State1, #{huffman => false}), ++ << 16#4803333037c1c0bf:64 >> = iolist_to_binary(Raw2), ++ {Huff2, State2} = encode(Headers2, State1), ++ << 16#4883640effc1c0bf:64 >> = iolist_to_binary(Huff2), ++ #state{size=180, dyn_table=[ ++ {42,{<<":status">>, <<"307">>}}, ++ {63,{<<"location">>, <<"https://www.example.com">>}}, ++ {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}}, ++ {52,{<<"cache-control">>, <<"private">>}}]} = State2, ++ %% Third response (raw then huffman). ++ Headers3 = [ ++ {<<":status">>, <<"200">>}, ++ {<<"cache-control">>, <<"private">>}, ++ {<<"date">>, <<"Mon, 21 Oct 2013 20:13:22 GMT">>}, ++ {<<"location">>, <<"https://www.example.com">>}, ++ {<<"content-encoding">>, <<"gzip">>}, ++ {<<"set-cookie">>, <<"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1">>} ++ ], ++ {Raw3, State3} = encode(Headers3, State2, #{huffman => false}), ++ << 16#88c1611d4d6f6e2c203231204f637420323031332032303a31333a323220474d54c05a04677a69707738666f6f3d4153444a4b48514b425a584f5157454f50495541585157454f49553b206d61782d6167653d333630303b2076657273696f6e3d31:784 >> = iolist_to_binary(Raw3), ++ {Huff3, State3} = encode(Headers3, State2), ++ << 16#88c16196d07abe941054d444a8200595040b8166e084a62d1bffc05a839bd9ab77ad94e7821dd7f2e6c7b335dfdfcd5b3960d5af27087f3672c1ab270fb5291f9587316065c003ed4ee5b1063d5007:632 >> = iolist_to_binary(Huff3), ++ #state{size=117, dyn_table=[ ++ {98,{<<"set-cookie">>, <<"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1">>}}, ++ {52,{<<"content-encoding">>, <<"gzip">>}}, ++ {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:22 GMT">>}}]} = State3, ++ ok. ++-endif. ++ ++%% Static and dynamic tables. ++ ++%% @todo There must be a more efficient way. ++table_find({Name, Value}, State) -> ++ case table_find_field({Name, iolist_to_binary(Value)}, State) of ++ not_found -> ++ case table_find_name(Name, State) of ++ NotFound = not_found -> ++ NotFound; ++ Found -> ++ {name, Found} ++ end; ++ Found -> ++ {field, Found} ++ end. ++ ++table_find_field({<<":authority">>, <<>>}, _) -> 1; ++table_find_field({<<":method">>, <<"GET">>}, _) -> 2; ++table_find_field({<<":method">>, <<"POST">>}, _) -> 3; ++table_find_field({<<":path">>, <<"/">>}, _) -> 4; ++table_find_field({<<":path">>, <<"/index.html">>}, _) -> 5; ++table_find_field({<<":scheme">>, <<"http">>}, _) -> 6; ++table_find_field({<<":scheme">>, <<"https">>}, _) -> 7; ++table_find_field({<<":status">>, <<"200">>}, _) -> 8; ++table_find_field({<<":status">>, <<"204">>}, _) -> 9; ++table_find_field({<<":status">>, <<"206">>}, _) -> 10; ++table_find_field({<<":status">>, <<"304">>}, _) -> 11; ++table_find_field({<<":status">>, <<"400">>}, _) -> 12; ++table_find_field({<<":status">>, <<"404">>}, _) -> 13; ++table_find_field({<<":status">>, <<"500">>}, _) -> 14; ++table_find_field({<<"accept-charset">>, <<>>}, _) -> 15; ++table_find_field({<<"accept-encoding">>, <<"gzip, deflate">>}, _) -> 16; ++table_find_field({<<"accept-language">>, <<>>}, _) -> 17; ++table_find_field({<<"accept-ranges">>, <<>>}, _) -> 18; ++table_find_field({<<"accept">>, <<>>}, _) -> 19; ++table_find_field({<<"access-control-allow-origin">>, <<>>}, _) -> 20; ++table_find_field({<<"age">>, <<>>}, _) -> 21; ++table_find_field({<<"allow">>, <<>>}, _) -> 22; ++table_find_field({<<"authorization">>, <<>>}, _) -> 23; ++table_find_field({<<"cache-control">>, <<>>}, _) -> 24; ++table_find_field({<<"content-disposition">>, <<>>}, _) -> 25; ++table_find_field({<<"content-encoding">>, <<>>}, _) -> 26; ++table_find_field({<<"content-language">>, <<>>}, _) -> 27; ++table_find_field({<<"content-length">>, <<>>}, _) -> 28; ++table_find_field({<<"content-location">>, <<>>}, _) -> 29; ++table_find_field({<<"content-range">>, <<>>}, _) -> 30; ++table_find_field({<<"content-type">>, <<>>}, _) -> 31; ++table_find_field({<<"cookie">>, <<>>}, _) -> 32; ++table_find_field({<<"date">>, <<>>}, _) -> 33; ++table_find_field({<<"etag">>, <<>>}, _) -> 34; ++table_find_field({<<"expect">>, <<>>}, _) -> 35; ++table_find_field({<<"expires">>, <<>>}, _) -> 36; ++table_find_field({<<"from">>, <<>>}, _) -> 37; ++table_find_field({<<"host">>, <<>>}, _) -> 38; ++table_find_field({<<"if-match">>, <<>>}, _) -> 39; ++table_find_field({<<"if-modified-since">>, <<>>}, _) -> 40; ++table_find_field({<<"if-none-match">>, <<>>}, _) -> 41; ++table_find_field({<<"if-range">>, <<>>}, _) -> 42; ++table_find_field({<<"if-unmodified-since">>, <<>>}, _) -> 43; ++table_find_field({<<"last-modified">>, <<>>}, _) -> 44; ++table_find_field({<<"link">>, <<>>}, _) -> 45; ++table_find_field({<<"location">>, <<>>}, _) -> 46; ++table_find_field({<<"max-forwards">>, <<>>}, _) -> 47; ++table_find_field({<<"proxy-authenticate">>, <<>>}, _) -> 48; ++table_find_field({<<"proxy-authorization">>, <<>>}, _) -> 49; ++table_find_field({<<"range">>, <<>>}, _) -> 50; ++table_find_field({<<"referer">>, <<>>}, _) -> 51; ++table_find_field({<<"refresh">>, <<>>}, _) -> 52; ++table_find_field({<<"retry-after">>, <<>>}, _) -> 53; ++table_find_field({<<"server">>, <<>>}, _) -> 54; ++table_find_field({<<"set-cookie">>, <<>>}, _) -> 55; ++table_find_field({<<"strict-transport-security">>, <<>>}, _) -> 56; ++table_find_field({<<"transfer-encoding">>, <<>>}, _) -> 57; ++table_find_field({<<"user-agent">>, <<>>}, _) -> 58; ++table_find_field({<<"vary">>, <<>>}, _) -> 59; ++table_find_field({<<"via">>, <<>>}, _) -> 60; ++table_find_field({<<"www-authenticate">>, <<>>}, _) -> 61; ++table_find_field(Header, #state{dyn_table=DynamicTable}) -> ++ table_find_field_dyn(Header, DynamicTable, 62). ++ ++table_find_field_dyn(_, [], _) -> not_found; ++table_find_field_dyn(Header, [{_, Header}|_], Index) -> Index; ++table_find_field_dyn(Header, [_|Tail], Index) -> table_find_field_dyn(Header, Tail, Index + 1). ++ ++table_find_name(<<":authority">>, _) -> 1; ++table_find_name(<<":method">>, _) -> 2; ++table_find_name(<<":path">>, _) -> 4; ++table_find_name(<<":scheme">>, _) -> 6; ++table_find_name(<<":status">>, _) -> 8; ++table_find_name(<<"accept-charset">>, _) -> 15; ++table_find_name(<<"accept-encoding">>, _) -> 16; ++table_find_name(<<"accept-language">>, _) -> 17; ++table_find_name(<<"accept-ranges">>, _) -> 18; ++table_find_name(<<"accept">>, _) -> 19; ++table_find_name(<<"access-control-allow-origin">>, _) -> 20; ++table_find_name(<<"age">>, _) -> 21; ++table_find_name(<<"allow">>, _) -> 22; ++table_find_name(<<"authorization">>, _) -> 23; ++table_find_name(<<"cache-control">>, _) -> 24; ++table_find_name(<<"content-disposition">>, _) -> 25; ++table_find_name(<<"content-encoding">>, _) -> 26; ++table_find_name(<<"content-language">>, _) -> 27; ++table_find_name(<<"content-length">>, _) -> 28; ++table_find_name(<<"content-location">>, _) -> 29; ++table_find_name(<<"content-range">>, _) -> 30; ++table_find_name(<<"content-type">>, _) -> 31; ++table_find_name(<<"cookie">>, _) -> 32; ++table_find_name(<<"date">>, _) -> 33; ++table_find_name(<<"etag">>, _) -> 34; ++table_find_name(<<"expect">>, _) -> 35; ++table_find_name(<<"expires">>, _) -> 36; ++table_find_name(<<"from">>, _) -> 37; ++table_find_name(<<"host">>, _) -> 38; ++table_find_name(<<"if-match">>, _) -> 39; ++table_find_name(<<"if-modified-since">>, _) -> 40; ++table_find_name(<<"if-none-match">>, _) -> 41; ++table_find_name(<<"if-range">>, _) -> 42; ++table_find_name(<<"if-unmodified-since">>, _) -> 43; ++table_find_name(<<"last-modified">>, _) -> 44; ++table_find_name(<<"link">>, _) -> 45; ++table_find_name(<<"location">>, _) -> 46; ++table_find_name(<<"max-forwards">>, _) -> 47; ++table_find_name(<<"proxy-authenticate">>, _) -> 48; ++table_find_name(<<"proxy-authorization">>, _) -> 49; ++table_find_name(<<"range">>, _) -> 50; ++table_find_name(<<"referer">>, _) -> 51; ++table_find_name(<<"refresh">>, _) -> 52; ++table_find_name(<<"retry-after">>, _) -> 53; ++table_find_name(<<"server">>, _) -> 54; ++table_find_name(<<"set-cookie">>, _) -> 55; ++table_find_name(<<"strict-transport-security">>, _) -> 56; ++table_find_name(<<"transfer-encoding">>, _) -> 57; ++table_find_name(<<"user-agent">>, _) -> 58; ++table_find_name(<<"vary">>, _) -> 59; ++table_find_name(<<"via">>, _) -> 60; ++table_find_name(<<"www-authenticate">>, _) -> 61; ++table_find_name(Name, #state{dyn_table=DynamicTable}) -> ++ table_find_name_dyn(Name, DynamicTable, 62). ++ ++table_find_name_dyn(_, [], _) -> not_found; ++table_find_name_dyn(Name, [{Name, _}|_], Index) -> Index; ++table_find_name_dyn(Name, [_|Tail], Index) -> table_find_name_dyn(Name, Tail, Index + 1). ++ ++table_get(1, _) -> {<<":authority">>, <<>>}; ++table_get(2, _) -> {<<":method">>, <<"GET">>}; ++table_get(3, _) -> {<<":method">>, <<"POST">>}; ++table_get(4, _) -> {<<":path">>, <<"/">>}; ++table_get(5, _) -> {<<":path">>, <<"/index.html">>}; ++table_get(6, _) -> {<<":scheme">>, <<"http">>}; ++table_get(7, _) -> {<<":scheme">>, <<"https">>}; ++table_get(8, _) -> {<<":status">>, <<"200">>}; ++table_get(9, _) -> {<<":status">>, <<"204">>}; ++table_get(10, _) -> {<<":status">>, <<"206">>}; ++table_get(11, _) -> {<<":status">>, <<"304">>}; ++table_get(12, _) -> {<<":status">>, <<"400">>}; ++table_get(13, _) -> {<<":status">>, <<"404">>}; ++table_get(14, _) -> {<<":status">>, <<"500">>}; ++table_get(15, _) -> {<<"accept-charset">>, <<>>}; ++table_get(16, _) -> {<<"accept-encoding">>, <<"gzip, deflate">>}; ++table_get(17, _) -> {<<"accept-language">>, <<>>}; ++table_get(18, _) -> {<<"accept-ranges">>, <<>>}; ++table_get(19, _) -> {<<"accept">>, <<>>}; ++table_get(20, _) -> {<<"access-control-allow-origin">>, <<>>}; ++table_get(21, _) -> {<<"age">>, <<>>}; ++table_get(22, _) -> {<<"allow">>, <<>>}; ++table_get(23, _) -> {<<"authorization">>, <<>>}; ++table_get(24, _) -> {<<"cache-control">>, <<>>}; ++table_get(25, _) -> {<<"content-disposition">>, <<>>}; ++table_get(26, _) -> {<<"content-encoding">>, <<>>}; ++table_get(27, _) -> {<<"content-language">>, <<>>}; ++table_get(28, _) -> {<<"content-length">>, <<>>}; ++table_get(29, _) -> {<<"content-location">>, <<>>}; ++table_get(30, _) -> {<<"content-range">>, <<>>}; ++table_get(31, _) -> {<<"content-type">>, <<>>}; ++table_get(32, _) -> {<<"cookie">>, <<>>}; ++table_get(33, _) -> {<<"date">>, <<>>}; ++table_get(34, _) -> {<<"etag">>, <<>>}; ++table_get(35, _) -> {<<"expect">>, <<>>}; ++table_get(36, _) -> {<<"expires">>, <<>>}; ++table_get(37, _) -> {<<"from">>, <<>>}; ++table_get(38, _) -> {<<"host">>, <<>>}; ++table_get(39, _) -> {<<"if-match">>, <<>>}; ++table_get(40, _) -> {<<"if-modified-since">>, <<>>}; ++table_get(41, _) -> {<<"if-none-match">>, <<>>}; ++table_get(42, _) -> {<<"if-range">>, <<>>}; ++table_get(43, _) -> {<<"if-unmodified-since">>, <<>>}; ++table_get(44, _) -> {<<"last-modified">>, <<>>}; ++table_get(45, _) -> {<<"link">>, <<>>}; ++table_get(46, _) -> {<<"location">>, <<>>}; ++table_get(47, _) -> {<<"max-forwards">>, <<>>}; ++table_get(48, _) -> {<<"proxy-authenticate">>, <<>>}; ++table_get(49, _) -> {<<"proxy-authorization">>, <<>>}; ++table_get(50, _) -> {<<"range">>, <<>>}; ++table_get(51, _) -> {<<"referer">>, <<>>}; ++table_get(52, _) -> {<<"refresh">>, <<>>}; ++table_get(53, _) -> {<<"retry-after">>, <<>>}; ++table_get(54, _) -> {<<"server">>, <<>>}; ++table_get(55, _) -> {<<"set-cookie">>, <<>>}; ++table_get(56, _) -> {<<"strict-transport-security">>, <<>>}; ++table_get(57, _) -> {<<"transfer-encoding">>, <<>>}; ++table_get(58, _) -> {<<"user-agent">>, <<>>}; ++table_get(59, _) -> {<<"vary">>, <<>>}; ++table_get(60, _) -> {<<"via">>, <<>>}; ++table_get(61, _) -> {<<"www-authenticate">>, <<>>}; ++table_get(Index, #state{dyn_table=DynamicTable}) -> ++ {_, Header} = lists:nth(Index - 61, DynamicTable), ++ Header. ++ ++table_get_name(1, _) -> <<":authority">>; ++table_get_name(2, _) -> <<":method">>; ++table_get_name(3, _) -> <<":method">>; ++table_get_name(4, _) -> <<":path">>; ++table_get_name(5, _) -> <<":path">>; ++table_get_name(6, _) -> <<":scheme">>; ++table_get_name(7, _) -> <<":scheme">>; ++table_get_name(8, _) -> <<":status">>; ++table_get_name(9, _) -> <<":status">>; ++table_get_name(10, _) -> <<":status">>; ++table_get_name(11, _) -> <<":status">>; ++table_get_name(12, _) -> <<":status">>; ++table_get_name(13, _) -> <<":status">>; ++table_get_name(14, _) -> <<":status">>; ++table_get_name(15, _) -> <<"accept-charset">>; ++table_get_name(16, _) -> <<"accept-encoding">>; ++table_get_name(17, _) -> <<"accept-language">>; ++table_get_name(18, _) -> <<"accept-ranges">>; ++table_get_name(19, _) -> <<"accept">>; ++table_get_name(20, _) -> <<"access-control-allow-origin">>; ++table_get_name(21, _) -> <<"age">>; ++table_get_name(22, _) -> <<"allow">>; ++table_get_name(23, _) -> <<"authorization">>; ++table_get_name(24, _) -> <<"cache-control">>; ++table_get_name(25, _) -> <<"content-disposition">>; ++table_get_name(26, _) -> <<"content-encoding">>; ++table_get_name(27, _) -> <<"content-language">>; ++table_get_name(28, _) -> <<"content-length">>; ++table_get_name(29, _) -> <<"content-location">>; ++table_get_name(30, _) -> <<"content-range">>; ++table_get_name(31, _) -> <<"content-type">>; ++table_get_name(32, _) -> <<"cookie">>; ++table_get_name(33, _) -> <<"date">>; ++table_get_name(34, _) -> <<"etag">>; ++table_get_name(35, _) -> <<"expect">>; ++table_get_name(36, _) -> <<"expires">>; ++table_get_name(37, _) -> <<"from">>; ++table_get_name(38, _) -> <<"host">>; ++table_get_name(39, _) -> <<"if-match">>; ++table_get_name(40, _) -> <<"if-modified-since">>; ++table_get_name(41, _) -> <<"if-none-match">>; ++table_get_name(42, _) -> <<"if-range">>; ++table_get_name(43, _) -> <<"if-unmodified-since">>; ++table_get_name(44, _) -> <<"last-modified">>; ++table_get_name(45, _) -> <<"link">>; ++table_get_name(46, _) -> <<"location">>; ++table_get_name(47, _) -> <<"max-forwards">>; ++table_get_name(48, _) -> <<"proxy-authenticate">>; ++table_get_name(49, _) -> <<"proxy-authorization">>; ++table_get_name(50, _) -> <<"range">>; ++table_get_name(51, _) -> <<"referer">>; ++table_get_name(52, _) -> <<"refresh">>; ++table_get_name(53, _) -> <<"retry-after">>; ++table_get_name(54, _) -> <<"server">>; ++table_get_name(55, _) -> <<"set-cookie">>; ++table_get_name(56, _) -> <<"strict-transport-security">>; ++table_get_name(57, _) -> <<"transfer-encoding">>; ++table_get_name(58, _) -> <<"user-agent">>; ++table_get_name(59, _) -> <<"vary">>; ++table_get_name(60, _) -> <<"via">>; ++table_get_name(61, _) -> <<"www-authenticate">>; ++table_get_name(Index, #state{dyn_table=DynamicTable}) -> ++ {_, {Name, _}} = lists:nth(Index - 61, DynamicTable), ++ Name. ++ ++table_insert(Entry = {Name, Value}, State=#state{size=Size, max_size=MaxSize, dyn_table=DynamicTable}) -> ++ EntrySize = byte_size(Name) + byte_size(Value) + 32, ++ Size2 = Size + EntrySize, ++ {DynamicTable2, Size3} = if ++ Size + EntrySize > MaxSize -> ++ table_resize(DynamicTable, MaxSize - EntrySize, 0, []); ++ true -> ++ {DynamicTable, Size2} ++ end, ++ State#state{size=Size3, dyn_table=[{EntrySize, Entry}|DynamicTable2]}. ++ ++table_resize([], _, Size, Acc) -> ++ {lists:reverse(Acc), Size}; ++table_resize([{EntrySize, _}|_], MaxSize, Size, Acc) when Size + EntrySize > MaxSize -> ++ {lists:reverse(Acc), Size}; ++table_resize([Entry = {EntrySize, _}|Tail], MaxSize, Size, Acc) -> ++ table_resize(Tail, MaxSize, Size + EntrySize, [Entry|Acc]). ++ ++table_update_size(0, State) -> ++ State#state{size=0, max_size=0, dyn_table=[]}; ++table_update_size(MaxSize, State=#state{max_size=MaxSize}) -> ++ State; ++table_update_size(MaxSize, #state{dyn_table=DynTable}) -> ++ {DynTable2, Size} = table_resize(DynTable, MaxSize, 0, []), ++ #state{size=Size, max_size=MaxSize, dyn_table=DynTable2}. ++ ++-ifdef(TEST). ++prop_str_raw() -> ++ ?FORALL(Str, binary(), begin ++ {Str, <<>>} =:= dec_str(iolist_to_binary(enc_str(Str, #{huffman => false}))) ++ end). ++ ++prop_str_huffman() -> ++ ?FORALL(Str, binary(), begin ++ {Str, <<>>} =:= dec_str(iolist_to_binary(enc_str(Str, #{huffman => true}))) ++ end). ++-endif. diff --git a/erlang-cowlib-0002-WS-accepts-iodata-so-byte_size-won-t-work-unless-we-.patch b/erlang-cowlib-0002-WS-accepts-iodata-so-byte_size-won-t-work-unless-we-.patch new file mode 100644 index 0000000..4f1b6f6 --- /dev/null +++ b/erlang-cowlib-0002-WS-accepts-iodata-so-byte_size-won-t-work-unless-we-.patch @@ -0,0 +1,18 @@ +From: Rob Ashton +Date: Thu, 9 Apr 2015 18:01:38 +0100 +Subject: [PATCH] WS accepts iodata, so byte_size won't work unless we flatten + + +diff --git a/src/cow_ws.erl b/src/cow_ws.erl +index c89c17a..c025b38 100644 +--- a/src/cow_ws.erl ++++ b/src/cow_ws.erl +@@ -580,7 +580,7 @@ masked_frame({binary, Payload}, _) -> + [<< 1:1, 0:3, 2:4, 1:1, Len/bits >>, MaskKeyBin, mask(iolist_to_binary(Payload), MaskKey, <<>>)]. + + payload_length(Payload) -> +- case byte_size(Payload) of ++ case iolist_size(Payload) of + N when N =< 125 -> << N:7 >>; + N when N =< 16#ffff -> << 126:7, N:16 >>; + N when N =< 16#7fffffffffffffff -> << 127:7, N:64 >> diff --git a/erlang-cowlib-0003-Add-cowboy_http2-with-initial-HTTP-2-suppport.patch b/erlang-cowlib-0003-Add-cowboy_http2-with-initial-HTTP-2-suppport.patch new file mode 100644 index 0000000..b60d3c9 --- /dev/null +++ b/erlang-cowlib-0003-Add-cowboy_http2-with-initial-HTTP-2-suppport.patch @@ -0,0 +1,348 @@ +From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= +Date: Thu, 11 Jun 2015 14:04:21 +0200 +Subject: [PATCH] Add cowboy_http2 with initial HTTP/2 suppport + + +diff --git a/src/cow_http2.erl b/src/cow_http2.erl +new file mode 100644 +index 0000000..a4d2e23 +--- /dev/null ++++ b/src/cow_http2.erl +@@ -0,0 +1,337 @@ ++%% Copyright (c) 2015, Loïc Hoguin ++%% ++%% Permission to use, copy, modify, and/or distribute this software for any ++%% purpose with or without fee is hereby granted, provided that the above ++%% copyright notice and this permission notice appear in all copies. ++%% ++%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++-module(cow_http2). ++ ++%% Parsing. ++-export([parse/1]). ++ ++%% Building. ++-export([data/3]). ++-export([headers/3]). ++-export([rst_stream/2]). ++-export([settings/1]). ++-export([push_promise/3]). ++-export([ping_ack/1]). ++ ++-type streamid() :: pos_integer(). ++-type fin() :: fin | nofin. ++-type head_fin() :: head_fin | head_nofin. ++-type exclusive() :: exclusive | shared. ++-type weight() :: 1..256. ++-type settings() :: map(). ++ ++-type error() :: no_error ++ | protocol_error ++ | internal_error ++ | flow_control_error ++ | settings_timeout ++ | stream_closed ++ | frame_size_error ++ | refused_stream ++ | cancel ++ | compression_error ++ | connect_error ++ | enhance_your_calm ++ | inadequate_security ++ | http_1_1_required ++ | unknown_error. ++-export_type([error/0]). ++ ++-type frame() :: {data, streamid(), fin(), binary()} ++ | {headers, streamid(), fin(), head_fin(), binary()} ++ | {headers, streamid(), fin(), head_fin(), exclusive(), streamid(), weight(), binary()} ++ | {priority, streamid(), exclusive(), streamid(), weight()} ++ | {rst_stream, streamid(), error()} ++ | {settings, settings()} ++ | settings_ack ++ | {push_promise, streamid(), head_fin(), streamid(), binary()} ++ | {ping, integer()} ++ | {ping_ack, integer()} ++ | {goaway, streamid(), error(), binary()} ++ | {window_update, non_neg_integer()} ++ | {window_update, streamid(), non_neg_integer()} ++ | {continuation, streamid(), head_fin(), binary()}. ++-export_type([frame/0]). ++ ++%% Parsing. ++ ++%% ++%% DATA frames. ++%% ++parse(<< _:24, 0:8, _:9, 0:31, _/bits >>) -> ++ {connection_error, protocol_error, 'DATA frames MUST be associated with a stream. (RFC7540 6.1)'}; ++parse(<< Len0:24, 0:8, _:4, 1:1, _:35, PadLen:8, _/bits >>) when PadLen >= Len0 -> ++ {connection_error, protocol_error, 'Length of padding MUST be less than length of payload. (RFC7540 6.1)'}; ++%% No padding. ++parse(<< Len:24, 0:8, _:4, 0:1, _:2, FlagEndStream:1, _:1, StreamID:31, Data:Len/binary, Rest/bits >>) -> ++ {ok, {data, StreamID, parse_fin(FlagEndStream), Data}, Rest}; ++%% Padding. ++parse(<< Len0:24, 0:8, _:4, 1:1, _:2, FlagEndStream:1, _:1, StreamID:31, PadLen:8, Rest0/bits >>) ++ when byte_size(Rest0) >= Len0 - 1 -> ++ Len = Len0 - PadLen, ++ case Rest0 of ++ << Data:Len/binary, 0:PadLen/binary, Rest/bits >> -> ++ {ok, {data, StreamID, parse_fin(FlagEndStream), Data}, Rest}; ++ _ -> ++ {connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.1)'} ++ end; ++%% ++%% HEADERS frames. ++%% ++parse(<< _:24, 1:8, _:9, 0:31, _/bits >>) -> ++ {connection_error, protocol_error, 'HEADERS frames MUST be associated with a stream. (RFC7540 6.2)'}; ++parse(<< Len0:24, 1:8, _:4, 1:1, _:35, PadLen:8, _/bits >>) when PadLen >= Len0 -> ++ {connection_error, protocol_error, 'Length of padding MUST be less than length of payload. (RFC7540 6.2)'}; ++parse(<< Len0:24, 1:8, _:2, 1:1, _:1, 1:1, _:35, PadLen:8, _/bits >>) when PadLen >= Len0 - 5 -> ++ {connection_error, protocol_error, 'Length of padding MUST be less than length of payload. (RFC7540 6.2)'}; ++%% No padding, no priority. ++parse(<< Len:24, 1:8, _:2, 0:1, _:1, 0:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31, ++ HeaderBlockFragment:Len/binary, Rest/bits >>) -> ++ {ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), HeaderBlockFragment}, Rest}; ++%% Padding, no priority. ++parse(<< Len0:24, 1:8, _:2, 0:1, _:1, 1:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31, ++ PadLen:8, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 1 -> ++ Len = Len0 - PadLen - 1, ++ case Rest0 of ++ << HeaderBlockFragment:Len/binary, 0:PadLen/binary, Rest/bits >> -> ++ {ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), HeaderBlockFragment}, Rest}; ++ _ -> ++ {connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.2)'} ++ end; ++%% No padding, priority. ++parse(<< Len0:24, 1:8, _:2, 1:1, _:1, 0:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31, ++ E:1, DepStreamID:31, Weight:8, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 5 -> ++ Len = Len0 - 5, ++ << HeaderBlockFragment:Len/binary, Rest/bits >> = Rest0, ++ {ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), ++ parse_exclusive(E), DepStreamID, Weight + 1, HeaderBlockFragment}, Rest}; ++%% Padding, priority. ++parse(<< Len0:24, 1:8, _:2, 1:1, _:1, 1:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31, ++ PadLen:8, E:1, DepStreamID:31, Weight:8, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 6 -> ++ Len = Len0 - PadLen - 6, ++ case Rest0 of ++ << HeaderBlockFragment:Len/binary, 0:PadLen/binary, Rest/bits >> -> ++ {ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), ++ parse_exclusive(E), DepStreamID, Weight + 1, HeaderBlockFragment}, Rest}; ++ _ -> ++ {connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.2)'} ++ end; ++%% ++%% PRIORITY frames. ++%% ++parse(<< 5:24, 2:8, _:9, 0:31, _/bits >>) -> ++ {connection_error, protocol_error, 'PRIORITY frames MUST be associated with a stream. (RFC7540 6.3)'}; ++parse(<< 5:24, 2:8, _:9, StreamID:31, E:1, DepStreamID:31, Weight:8, Rest/bits >>) -> ++ {ok, {priority, StreamID, parse_exclusive(E), DepStreamID, Weight + 1}, Rest}; ++%% @todo figure out how to best deal with frame size errors; if we have everything fine ++%% if not we might want to inform the caller how much he should expect so that it can ++%% decide if it should just close the connection ++parse(<< BadLen:24, 2:8, _:9, StreamID:31, _:BadLen/binary, Rest/bits >>) -> ++ {stream_error, StreamID, frame_size_error, 'PRIORITY frames MUST be 5 bytes wide. (RFC7540 6.3)', Rest}; ++%% ++%% RST_STREAM frames. ++%% ++parse(<< 4:24, 3:8, _:9, 0:31, _/bits >>) -> ++ {connection_error, protocol_error, 'RST_STREAM frames MUST be associated with a stream. (RFC7540 6.4)'}; ++parse(<< 4:24, 3:8, _:9, StreamID:31, ErrorCode:32, Rest/bits >>) -> ++ {ok, {rst_stream, StreamID, parse_error_code(ErrorCode)}, Rest}; ++%% @todo same as priority ++parse(<< BadLen:24, 3:8, _:9, StreamID:31, _:BadLen/binary, Rest/bits >>) -> ++ {stream_error, StreamID, frame_size_error, 'RST_STREAM frames MUST be 4 bytes wide. (RFC7540 6.4)', Rest}; ++%% ++%% SETTINGS frames. ++%% ++parse(<< 0:24, 4:8, _:7, 1:1, _:1, 0:31, Rest/bits >>) -> ++ {ok, settings_ack, Rest}; ++parse(<< _:24, 4:8, _:7, 1:1, _:1, 0:31, _/bits >>) -> ++ {connection_error, frame_size_error, 'SETTINGS frames with the ACK flag set MUST have a length of 0. (RFC7540 6.5)'}; ++parse(<< Len:24, 4:8, _:7, 0:1, _:1, 0:31, _/bits >>) when Len rem 6 =/= 0 -> ++ {connection_error, frame_size_error, 'SETTINGS frames MUST have a length multiple of 6. (RFC7540 6.5)'}; ++parse(<< Len:24, 4:8, _:7, 0:1, _:1, 0:31, Rest/bits >>) when byte_size(Rest) >= Len -> ++ parse_settings(Rest, Len, #{}); ++parse(<< _:24, 4:8, _/bits >>) -> ++ {connection_error, protocol_error, 'SETTINGS frames MUST NOT be associated with a stream. (RFC7540 6.5)'}; ++%% ++%% PUSH_PROMISE frames. ++%% ++parse(<< _:24, 5:8, _:9, 0:31, _/bits >>) -> ++ {connection_error, protocol_error, 'PUSH_PROMISE frames MUST be associated with a stream. (RFC7540 6.6)'}; ++parse(<< Len0:24, 5:8, _:4, 0:1, FlagEndHeaders:1, _:3, StreamID:31, _:1, PromisedStreamID:31, Rest0/bits >>) ++ when byte_size(Rest0) >= Len0 - 4 -> ++ Len = Len0 - 4, ++ << HeaderBlockFragment:Len/binary, Rest/bits >> = Rest0, ++ {ok, {push_promise, StreamID, parse_head_fin(FlagEndHeaders), PromisedStreamID, HeaderBlockFragment}, Rest}; ++parse(<< Len0:24, 5:8, _:4, 1:1, FlagEndHeaders:1, _:2, StreamID:31, PadLen:8, _:1, PromisedStreamID:31, Rest0/bits >>) ++ when byte_size(Rest0) >= Len0 - 5 -> ++ Len = Len0 - 5, ++ case Rest0 of ++ << HeaderBlockFragment:Len/binary, 0:PadLen/binary, Rest/bits >> -> ++ {ok, {push_promise, StreamID, parse_head_fin(FlagEndHeaders), PromisedStreamID, HeaderBlockFragment}, Rest}; ++ _ -> ++ {connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.6)'} ++ end; ++%% ++%% PING frames. ++%% ++parse(<< 8:24, 6:8, _:7, 1:1, _:1, 0:31, Opaque:64, Rest/bits >>) -> ++ {ok, {ping_ack, Opaque}, Rest}; ++parse(<< 8:24, 6:8, _:7, 0:1, _:1, 0:31, Opaque:64, Rest/bits >>) -> ++ {ok, {ping, Opaque}, Rest}; ++parse(<< 8:24, 6:8, _:104, _/bits >>) -> ++ {connection_error, protocol_error, 'PING frames MUST NOT be associated with a stream. (RFC7540 6.7)'}; ++parse(<< _:24, 6:8, _/bits >>) -> ++ {connection_error, frame_size_error, 'PING frames MUST be 8 bytes wide. (RFC7540 6.7)'}; ++%% ++%% GOAWAY frames. ++%% ++parse(<< Len0:24, 7:8, _:9, 0:31, _:1, LastStreamID:31, ErrorCode:32, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 8 -> ++ Len = Len0 - 8, ++ << DebugData:Len/binary, Rest/bits >> = Rest0, ++ {ok, {goaway, LastStreamID, parse_error_code(ErrorCode), DebugData}, Rest}; ++parse(<< _:24, 7:8, _:40, _/bits >>) -> ++ {connection_error, protocol_error, 'GOAWAY frames MUST NOT be associated with a stream. (RFC7540 6.8)'}; ++%% ++%% WINDOW_UPDATE frames. ++%% ++parse(<< 4:24, 8:8, _:9, 0:31, _:1, 0:31, _/bits >>) -> ++ {connection_error, protocol_error, 'WINDOW_UPDATE frames MUST have a non-zero increment. (RFC7540 6.9)'}; ++parse(<< 4:24, 8:8, _:9, 0:31, _:1, Increment:31, Rest/bits >>) -> ++ {ok, {window_update, Increment}, Rest}; ++parse(<< 4:24, 8:8, _:9, StreamID:31, _:1, 0:31, _/bits >>) -> ++ {stream_error, StreamID, protocol_error, 'WINDOW_UPDATE frames MUST have a non-zero increment. (RFC7540 6.9)'}; ++parse(<< 4:24, 8:8, _:9, StreamID:31, _:1, Increment:31, Rest/bits >>) -> ++ {ok, {window_update, StreamID, Increment}, Rest}; ++parse(<< _:24, 8:8, _/bits >>) -> ++ {connection_error, frame_size_error, 'WINDOW_UPDATE frames MUST be 4 bytes wide. (RFC7540 6.9)'}; ++%% ++%% CONTINUATION frames. ++%% ++parse(<< _:24, 9:8, _:9, 0:31, _/bits >>) -> ++ {connection_error, protocol_error, 'CONTINUATION frames MUST be associated with a stream. (RFC7540 6.10)'}; ++parse(<< Len:24, 9:8, _:5, FlagEndHeaders:1, _:3, StreamID:31, HeaderBlockFragment:Len/binary, Rest/bits >>) -> ++ {ok, {continuation, StreamID, parse_head_fin(FlagEndHeaders), HeaderBlockFragment}, Rest}; ++%% ++%% Incomplete frames. ++%% ++parse(_) -> ++ more. ++ ++parse_fin(0) -> nofin; ++parse_fin(1) -> fin. ++ ++parse_head_fin(0) -> head_nofin; ++parse_head_fin(1) -> head_fin. ++ ++parse_exclusive(0) -> shared; ++parse_exclusive(1) -> exclusive. ++ ++parse_error_code( 0) -> no_error; ++parse_error_code( 1) -> protocol_error; ++parse_error_code( 2) -> internal_error; ++parse_error_code( 3) -> flow_control_error; ++parse_error_code( 4) -> settings_timeout; ++parse_error_code( 5) -> stream_closed; ++parse_error_code( 6) -> frame_size_error; ++parse_error_code( 7) -> refused_stream; ++parse_error_code( 8) -> cancel; ++parse_error_code( 9) -> compression_error; ++parse_error_code(10) -> connect_error; ++parse_error_code(11) -> enhance_your_calm; ++parse_error_code(12) -> inadequate_security; ++parse_error_code(13) -> http_1_1_required; ++parse_error_code(_) -> unknown_error. ++ ++parse_settings(Rest, 0, Settings) -> ++ {ok, {settings, Settings}, Rest}; ++%% SETTINGS_HEADER_TABLE_SIZE. ++parse_settings(<< 1:16, Value:32, Rest/bits >>, Len, Settings) -> ++ parse_settings(Rest, Len - 6, Settings#{header_table_size => Value}); ++%% SETTINGS_ENABLE_PUSH. ++parse_settings(<< 2:16, 0:32, Rest/bits >>, Len, Settings) -> ++ parse_settings(Rest, Len - 6, Settings#{enable_push => false}); ++parse_settings(<< 2:16, 1:32, Rest/bits >>, Len, Settings) -> ++ parse_settings(Rest, Len - 6, Settings#{enable_push => true}); ++parse_settings(<< 2:16, _:32, _/bits >>, _, _) -> ++ {connection_error, protocol_error, 'The SETTINGS_ENABLE_PUSH value MUST be 0 or 1. (RFC7540 6.5.2)'}; ++%% SETTINGS_MAX_CONCURRENT_STREAMS. ++parse_settings(<< 3:16, Value:32, Rest/bits >>, Len, Settings) -> ++ parse_settings(Rest, Len - 6, Settings#{max_concurrent_streams => Value}); ++%% SETTINGS_INITIAL_WINDOW_SIZE. ++parse_settings(<< 4:16, Value:32, _/bits >>, _, _) when Value > 16#7fffffff -> ++ {connection_error, flow_control_error, 'The maximum SETTINGS_INITIAL_WINDOW_SIZE value is 0x7fffffff. (RFC7540 6.5.2)'}; ++parse_settings(<< 4:16, Value:32, Rest/bits >>, Len, Settings) -> ++ parse_settings(Rest, Len - 6, Settings#{initial_window_size => Value}); ++%% SETTINGS_MAX_FRAME_SIZE. ++parse_settings(<< 5:16, Value:32, _/bits >>, _, _) when Value =< 16#3fff -> ++ {connection_error, protocol_error, 'The SETTINGS_MAX_FRAME_SIZE value must be > 0x3fff. (RFC7540 6.5.2)'}; ++parse_settings(<< 5:16, Value:32, Rest/bits >>, Len, Settings) when Value =< 16#ffffff -> ++ parse_settings(Rest, Len - 6, Settings#{max_frame_size => Value}); ++parse_settings(<< 5:16, _:32, _/bits >>, _, _) -> ++ {connection_error, protocol_error, 'The SETTINGS_MAX_FRAME_SIZE value must be =< 0xffffff. (RFC7540 6.5.2)'}; ++%% SETTINGS_MAX_HEADER_LIST_SIZE. ++parse_settings(<< 6:16, Value:32, Rest/bits >>, Len, Settings) -> ++ parse_settings(Rest, Len - 6, Settings#{max_header_list_size => Value}); ++parse_settings(<< _:48, Rest/bits >>, Len, Settings) -> ++ parse_settings(Rest, Len - 6, Settings). ++ ++%% Building. ++ ++%% @todo Check size and create multiple frames if needed. ++data(StreamID, IsFin, Data) -> ++ Len = iolist_size(Data), ++ FlagEndStream = flag_fin(IsFin), ++ [<< Len:24, 0:15, FlagEndStream:1, 0:1, StreamID:31 >>, Data]. ++ ++%% @todo Check size of HeaderBlock and use CONTINUATION frames if needed. ++headers(StreamID, IsFin, HeaderBlock) -> ++ Len = iolist_size(HeaderBlock), ++ FlagEndStream = flag_fin(IsFin), ++ FlagEndHeaders = 1, ++ [<< Len:24, 1:8, 0:5, FlagEndHeaders:1, 0:1, FlagEndStream:1, 0:1, StreamID:31 >>, HeaderBlock]. ++ ++rst_stream(StreamID, Reason) -> ++ ErrorCode = error_code(Reason), ++ << 4:24, 3:8, 0:9, StreamID:31, ErrorCode:32 >>. ++ ++%% @todo Actually implement it. :-) ++settings(#{}) -> ++ << 0:24, 4:8, 0:40 >>. ++ ++%% @todo Check size of HeaderBlock and use CONTINUATION frames if needed. ++push_promise(StreamID, PromisedStreamID, HeaderBlock) -> ++ Len = iolist_size(HeaderBlock) + 4, ++ FlagEndHeaders = 1, ++ [<< Len:24, 5:8, 0:5, FlagEndHeaders:1, 0:3, StreamID:31, 0:1, PromisedStreamID:31 >>, HeaderBlock]. ++ ++ping_ack(Opaque) -> ++ << 8:24, 6:8, 0:7, 1:1, 0:32, Opaque:64 >>. ++ ++flag_fin(nofin) -> 0; ++flag_fin(fin) -> 1. ++ ++error_code(no_error) -> 0; ++error_code(protocol_error) -> 1; ++error_code(internal_error) -> 2; ++error_code(flow_control_error) -> 3; ++error_code(settings_timeout) -> 4; ++error_code(stream_closed) -> 5; ++error_code(frame_size_error) -> 6; ++error_code(refused_stream) -> 7; ++error_code(cancel) -> 8; ++error_code(compression_error) -> 9; ++error_code(connect_error) -> 10; ++error_code(enhance_your_calm) -> 11; ++error_code(inadequate_security) -> 12; ++error_code(http_1_1_required) -> 13. diff --git a/erlang-cowlib-0004-Fix-handling-of-default-values-in-cookie-options.patch b/erlang-cowlib-0004-Fix-handling-of-default-values-in-cookie-options.patch new file mode 100644 index 0000000..7a56a01 --- /dev/null +++ b/erlang-cowlib-0004-Fix-handling-of-default-values-in-cookie-options.patch @@ -0,0 +1,40 @@ +From: Krzysztof Jurewicz +Date: Tue, 21 Jul 2015 11:10:47 +0200 +Subject: [PATCH] Fix handling of default values in cookie options +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Previously, an error would be raised when explicitly passing a default +value for either “http_only” or “secure” option. + +diff --git a/src/cow_cookie.erl b/src/cow_cookie.erl +index 150efeb..b31a528 100644 +--- a/src/cow_cookie.erl ++++ b/src/cow_cookie.erl +@@ -197,10 +197,12 @@ setcookie(Name, Value, Opts) -> + end, + SecureBin = case lists:keyfind(secure, 1, Opts) of + false -> <<>>; ++ {_, false} -> <<>>; + {_, true} -> <<"; Secure">> + end, + HttpOnlyBin = case lists:keyfind(http_only, 1, Opts) of + false -> <<>>; ++ {_, false} -> <<>>; + {_, true} -> <<"; HttpOnly">> + end, + [Name, <<"=">>, Value, <<"; Version=1">>, +@@ -218,6 +220,12 @@ setcookie_test_() -> + [{path, <<"/acme">>}], + <<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>}, + {<<"Customer">>, <<"WILE_E_COYOTE">>, ++ [{secure, true}], ++ <<"Customer=WILE_E_COYOTE; Version=1; Secure">>}, ++ {<<"Customer">>, <<"WILE_E_COYOTE">>, ++ [{secure, false}, {http_only, false}], ++ <<"Customer=WILE_E_COYOTE; Version=1">>}, ++ {<<"Customer">>, <<"WILE_E_COYOTE">>, + [{path, <<"/acme">>}, {badoption, <<"negatory">>}], + <<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>} + ], diff --git a/erlang-cowlib-0005-Fix-a-few-binary-pattern-matching-issues.patch b/erlang-cowlib-0005-Fix-a-few-binary-pattern-matching-issues.patch new file mode 100644 index 0000000..d846f05 --- /dev/null +++ b/erlang-cowlib-0005-Fix-a-few-binary-pattern-matching-issues.patch @@ -0,0 +1,45 @@ +From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= +Date: Wed, 26 Aug 2015 17:27:33 +0200 +Subject: [PATCH] Fix a few binary pattern matching issues + + +diff --git a/src/cow_http2.erl b/src/cow_http2.erl +index a4d2e23..c7773b4 100644 +--- a/src/cow_http2.erl ++++ b/src/cow_http2.erl +@@ -82,7 +82,7 @@ parse(<< Len0:24, 0:8, _:4, 1:1, _:2, FlagEndStream:1, _:1, StreamID:31, PadLen: + when byte_size(Rest0) >= Len0 - 1 -> + Len = Len0 - PadLen, + case Rest0 of +- << Data:Len/binary, 0:PadLen/binary, Rest/bits >> -> ++ << Data:Len/binary, 0:PadLen/unit:8, Rest/bits >> -> + {ok, {data, StreamID, parse_fin(FlagEndStream), Data}, Rest}; + _ -> + {connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.1)'} +@@ -105,7 +105,7 @@ parse(<< Len0:24, 1:8, _:2, 0:1, _:1, 1:1, FlagEndHeaders:1, _:1, FlagEndStream: + PadLen:8, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 1 -> + Len = Len0 - PadLen - 1, + case Rest0 of +- << HeaderBlockFragment:Len/binary, 0:PadLen/binary, Rest/bits >> -> ++ << HeaderBlockFragment:Len/binary, 0:PadLen/unit:8, Rest/bits >> -> + {ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), HeaderBlockFragment}, Rest}; + _ -> + {connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.2)'} +@@ -122,7 +122,7 @@ parse(<< Len0:24, 1:8, _:2, 1:1, _:1, 1:1, FlagEndHeaders:1, _:1, FlagEndStream: + PadLen:8, E:1, DepStreamID:31, Weight:8, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 6 -> + Len = Len0 - PadLen - 6, + case Rest0 of +- << HeaderBlockFragment:Len/binary, 0:PadLen/binary, Rest/bits >> -> ++ << HeaderBlockFragment:Len/binary, 0:PadLen/unit:8, Rest/bits >> -> + {ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), + parse_exclusive(E), DepStreamID, Weight + 1, HeaderBlockFragment}, Rest}; + _ -> +@@ -177,7 +177,7 @@ parse(<< Len0:24, 5:8, _:4, 1:1, FlagEndHeaders:1, _:2, StreamID:31, PadLen:8, _ + when byte_size(Rest0) >= Len0 - 5 -> + Len = Len0 - 5, + case Rest0 of +- << HeaderBlockFragment:Len/binary, 0:PadLen/binary, Rest/bits >> -> ++ << HeaderBlockFragment:Len/binary, 0:PadLen/unit:8, Rest/bits >> -> + {ok, {push_promise, StreamID, parse_head_fin(FlagEndHeaders), PromisedStreamID, HeaderBlockFragment}, Rest}; + _ -> + {connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.6)'} diff --git a/erlang-cowlib-0006-Add-cow_http-response-3.patch b/erlang-cowlib-0006-Add-cow_http-response-3.patch new file mode 100644 index 0000000..c3c8132 --- /dev/null +++ b/erlang-cowlib-0006-Add-cow_http-response-3.patch @@ -0,0 +1,96 @@ +From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= +Date: Sat, 12 Mar 2016 18:09:41 +0100 +Subject: [PATCH] Add cow_http:response/3 + + +diff --git a/src/cow_http.erl b/src/cow_http.erl +index 8f2ae92..4b98d81 100644 +--- a/src/cow_http.erl ++++ b/src/cow_http.erl +@@ -22,6 +22,7 @@ + -export([parse_version/1]). + + -export([request/4]). ++-export([response/3]). + -export([version/1]). + + -type version() :: 'HTTP/1.0' | 'HTTP/1.1'. +@@ -243,6 +244,12 @@ request(Method, Path, Version, Headers) -> + [[N, <<": ">>, V, <<"\r\n">>] || {N, V} <- Headers], + <<"\r\n">>]. + ++-spec response(status() | binary(), version(), headers()) -> iodata(). ++response(Status, Version, Headers) -> ++ [version(Version), <<" ">>, status(Status), <<"\r\n">>, ++ [[N, <<": ">>, V, <<"\r\n">>] || {N, V} <- Headers], ++ <<"\r\n">>]. ++ + %% @doc Return the version as a binary. + + -spec version(version()) -> binary(). +@@ -256,3 +263,65 @@ version_test() -> + {'EXIT', _} = (catch version('HTTP/1.2')), + ok. + -endif. ++ ++%% @doc Return the status code and string as binary. ++ ++-spec status(status() | binary()) -> binary(). ++status(100) -> <<"100 Continue">>; ++status(101) -> <<"101 Switching Protocols">>; ++status(102) -> <<"102 Processing">>; ++status(200) -> <<"200 OK">>; ++status(201) -> <<"201 Created">>; ++status(202) -> <<"202 Accepted">>; ++status(203) -> <<"203 Non-Authoritative Information">>; ++status(204) -> <<"204 No Content">>; ++status(205) -> <<"205 Reset Content">>; ++status(206) -> <<"206 Partial Content">>; ++status(207) -> <<"207 Multi-Status">>; ++status(226) -> <<"226 IM Used">>; ++status(300) -> <<"300 Multiple Choices">>; ++status(301) -> <<"301 Moved Permanently">>; ++status(302) -> <<"302 Found">>; ++status(303) -> <<"303 See Other">>; ++status(304) -> <<"304 Not Modified">>; ++status(305) -> <<"305 Use Proxy">>; ++status(306) -> <<"306 Switch Proxy">>; ++status(307) -> <<"307 Temporary Redirect">>; ++status(400) -> <<"400 Bad Request">>; ++status(401) -> <<"401 Unauthorized">>; ++status(402) -> <<"402 Payment Required">>; ++status(403) -> <<"403 Forbidden">>; ++status(404) -> <<"404 Not Found">>; ++status(405) -> <<"405 Method Not Allowed">>; ++status(406) -> <<"406 Not Acceptable">>; ++status(407) -> <<"407 Proxy Authentication Required">>; ++status(408) -> <<"408 Request Timeout">>; ++status(409) -> <<"409 Conflict">>; ++status(410) -> <<"410 Gone">>; ++status(411) -> <<"411 Length Required">>; ++status(412) -> <<"412 Precondition Failed">>; ++status(413) -> <<"413 Request Entity Too Large">>; ++status(414) -> <<"414 Request-URI Too Long">>; ++status(415) -> <<"415 Unsupported Media Type">>; ++status(416) -> <<"416 Requested Range Not Satisfiable">>; ++status(417) -> <<"417 Expectation Failed">>; ++status(418) -> <<"418 I'm a teapot">>; ++status(422) -> <<"422 Unprocessable Entity">>; ++status(423) -> <<"423 Locked">>; ++status(424) -> <<"424 Failed Dependency">>; ++status(425) -> <<"425 Unordered Collection">>; ++status(426) -> <<"426 Upgrade Required">>; ++status(428) -> <<"428 Precondition Required">>; ++status(429) -> <<"429 Too Many Requests">>; ++status(431) -> <<"431 Request Header Fields Too Large">>; ++status(500) -> <<"500 Internal Server Error">>; ++status(501) -> <<"501 Not Implemented">>; ++status(502) -> <<"502 Bad Gateway">>; ++status(503) -> <<"503 Service Unavailable">>; ++status(504) -> <<"504 Gateway Timeout">>; ++status(505) -> <<"505 HTTP Version Not Supported">>; ++status(506) -> <<"506 Variant Also Negotiates">>; ++status(507) -> <<"507 Insufficient Storage">>; ++status(510) -> <<"510 Not Extended">>; ++status(511) -> <<"511 Network Authentication Required">>; ++status(B) when is_binary(B) -> B. diff --git a/erlang-cowlib-0007-Add-settings_payload-1-settings_ack-0-and-ping-1-in-.patch b/erlang-cowlib-0007-Add-settings_payload-1-settings_ack-0-and-ping-1-in-.patch new file mode 100644 index 0000000..98a1b4c --- /dev/null +++ b/erlang-cowlib-0007-Add-settings_payload-1-settings_ack-0-and-ping-1-in-.patch @@ -0,0 +1,45 @@ +From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= +Date: Sat, 12 Mar 2016 18:10:32 +0100 +Subject: [PATCH] Add settings_payload/1, settings_ack/0 and ping/1 in + cow_http2 + +Although settings_payload/1 is currently only a placeholder. + +diff --git a/src/cow_http2.erl b/src/cow_http2.erl +index c7773b4..163802e 100644 +--- a/src/cow_http2.erl ++++ b/src/cow_http2.erl +@@ -22,7 +22,10 @@ + -export([headers/3]). + -export([rst_stream/2]). + -export([settings/1]). ++-export([settings_payload/1]). ++-export([settings_ack/0]). + -export([push_promise/3]). ++-export([ping/1]). + -export([ping_ack/1]). + + -type streamid() :: pos_integer(). +@@ -309,12 +312,22 @@ rst_stream(StreamID, Reason) -> + settings(#{}) -> + << 0:24, 4:8, 0:40 >>. + ++%% @todo Actually implement it. :-) ++settings_payload(#{}) -> ++ <<>>. ++ ++settings_ack() -> ++ << 0:24, 4:8, 1:8, 0:32 >>. ++ + %% @todo Check size of HeaderBlock and use CONTINUATION frames if needed. + push_promise(StreamID, PromisedStreamID, HeaderBlock) -> + Len = iolist_size(HeaderBlock) + 4, + FlagEndHeaders = 1, + [<< Len:24, 5:8, 0:5, FlagEndHeaders:1, 0:3, StreamID:31, 0:1, PromisedStreamID:31 >>, HeaderBlock]. + ++ping(Opaque) -> ++ << 8:24, 6:8, 0:40, Opaque:64 >>. ++ + ping_ack(Opaque) -> + << 8:24, 6:8, 0:7, 1:1, 0:32, Opaque:64 >>. + diff --git a/erlang-cowlib-0008-Add-cow_http_hd-parse_http2_settings-1.patch b/erlang-cowlib-0008-Add-cow_http_hd-parse_http2_settings-1.patch new file mode 100644 index 0000000..3e2f8ba --- /dev/null +++ b/erlang-cowlib-0008-Add-cow_http_hd-parse_http2_settings-1.patch @@ -0,0 +1,32 @@ +From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= +Date: Sat, 12 Mar 2016 18:21:39 +0100 +Subject: [PATCH] Add cow_http_hd:parse_http2_settings/1 + +No tests or benchmarks, we just call base64:decode on the value. + +diff --git a/src/cow_http_hd.erl b/src/cow_http_hd.erl +index e47d80d..e6c4524 100644 +--- a/src/cow_http_hd.erl ++++ b/src/cow_http_hd.erl +@@ -56,7 +56,7 @@ + % @todo -export([parse_forwarded/1]). RFC7239 + % @todo -export([parse_from/1]). RFC7231 + -export([parse_host/1]). +-% @todo -export([parse_http2_settings/1]). HTTP/2 (upcoming) ++-export([parse_http2_settings/1]). + -export([parse_if_match/1]). + -export([parse_if_modified_since/1]). + -export([parse_if_none_match/1]). +@@ -1885,6 +1885,12 @@ horse_parse_host_ipv6_v4() -> + ). + -endif. + ++%% @doc Parse the HTTP2-Settings header. ++ ++-spec parse_http2_settings(binary()) -> binary(). ++parse_http2_settings(HTTP2Settings) -> ++ base64:decode(HTTP2Settings). ++ + %% @doc Parse the If-Match header. + + -spec parse_if_match(binary()) -> '*' | [etag()]. diff --git a/erlang-cowlib-0009-Parse-the-settings-payload-directly-in-cow_http_hd-p.patch b/erlang-cowlib-0009-Parse-the-settings-payload-directly-in-cow_http_hd-p.patch new file mode 100644 index 0000000..3c727c3 --- /dev/null +++ b/erlang-cowlib-0009-Parse-the-settings-payload-directly-in-cow_http_hd-p.patch @@ -0,0 +1,105 @@ +From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= +Date: Sun, 13 Mar 2016 11:14:58 +0100 +Subject: [PATCH] Parse the settings payload directly in + cow_http_hd:parse_http2_settings/1 + + +diff --git a/src/cow_http2.erl b/src/cow_http2.erl +index 163802e..c8dbd9b 100644 +--- a/src/cow_http2.erl ++++ b/src/cow_http2.erl +@@ -16,6 +16,7 @@ + + %% Parsing. + -export([parse/1]). ++-export([parse_settings_payload/1]). + + %% Building. + -export([data/3]). +@@ -163,7 +164,7 @@ parse(<< _:24, 4:8, _:7, 1:1, _:1, 0:31, _/bits >>) -> + parse(<< Len:24, 4:8, _:7, 0:1, _:1, 0:31, _/bits >>) when Len rem 6 =/= 0 -> + {connection_error, frame_size_error, 'SETTINGS frames MUST have a length multiple of 6. (RFC7540 6.5)'}; + parse(<< Len:24, 4:8, _:7, 0:1, _:1, 0:31, Rest/bits >>) when byte_size(Rest) >= Len -> +- parse_settings(Rest, Len, #{}); ++ parse_settings_payload(Rest, Len, #{}); + parse(<< _:24, 4:8, _/bits >>) -> + {connection_error, protocol_error, 'SETTINGS frames MUST NOT be associated with a stream. (RFC7540 6.5)'}; + %% +@@ -256,38 +257,43 @@ parse_error_code(12) -> inadequate_security; + parse_error_code(13) -> http_1_1_required; + parse_error_code(_) -> unknown_error. + +-parse_settings(Rest, 0, Settings) -> ++parse_settings_payload(SettingsPayload) -> ++ {ok, {settings, Settings}, <<>>} ++ = parse_settings_payload(SettingsPayload, byte_size(SettingsPayload), #{}), ++ Settings. ++ ++parse_settings_payload(Rest, 0, Settings) -> + {ok, {settings, Settings}, Rest}; + %% SETTINGS_HEADER_TABLE_SIZE. +-parse_settings(<< 1:16, Value:32, Rest/bits >>, Len, Settings) -> +- parse_settings(Rest, Len - 6, Settings#{header_table_size => Value}); ++parse_settings_payload(<< 1:16, Value:32, Rest/bits >>, Len, Settings) -> ++ parse_settings_payload(Rest, Len - 6, Settings#{header_table_size => Value}); + %% SETTINGS_ENABLE_PUSH. +-parse_settings(<< 2:16, 0:32, Rest/bits >>, Len, Settings) -> +- parse_settings(Rest, Len - 6, Settings#{enable_push => false}); +-parse_settings(<< 2:16, 1:32, Rest/bits >>, Len, Settings) -> +- parse_settings(Rest, Len - 6, Settings#{enable_push => true}); +-parse_settings(<< 2:16, _:32, _/bits >>, _, _) -> ++parse_settings_payload(<< 2:16, 0:32, Rest/bits >>, Len, Settings) -> ++ parse_settings_payload(Rest, Len - 6, Settings#{enable_push => false}); ++parse_settings_payload(<< 2:16, 1:32, Rest/bits >>, Len, Settings) -> ++ parse_settings_payload(Rest, Len - 6, Settings#{enable_push => true}); ++parse_settings_payload(<< 2:16, _:32, _/bits >>, _, _) -> + {connection_error, protocol_error, 'The SETTINGS_ENABLE_PUSH value MUST be 0 or 1. (RFC7540 6.5.2)'}; + %% SETTINGS_MAX_CONCURRENT_STREAMS. +-parse_settings(<< 3:16, Value:32, Rest/bits >>, Len, Settings) -> +- parse_settings(Rest, Len - 6, Settings#{max_concurrent_streams => Value}); ++parse_settings_payload(<< 3:16, Value:32, Rest/bits >>, Len, Settings) -> ++ parse_settings_payload(Rest, Len - 6, Settings#{max_concurrent_streams => Value}); + %% SETTINGS_INITIAL_WINDOW_SIZE. +-parse_settings(<< 4:16, Value:32, _/bits >>, _, _) when Value > 16#7fffffff -> ++parse_settings_payload(<< 4:16, Value:32, _/bits >>, _, _) when Value > 16#7fffffff -> + {connection_error, flow_control_error, 'The maximum SETTINGS_INITIAL_WINDOW_SIZE value is 0x7fffffff. (RFC7540 6.5.2)'}; +-parse_settings(<< 4:16, Value:32, Rest/bits >>, Len, Settings) -> +- parse_settings(Rest, Len - 6, Settings#{initial_window_size => Value}); ++parse_settings_payload(<< 4:16, Value:32, Rest/bits >>, Len, Settings) -> ++ parse_settings_payload(Rest, Len - 6, Settings#{initial_window_size => Value}); + %% SETTINGS_MAX_FRAME_SIZE. +-parse_settings(<< 5:16, Value:32, _/bits >>, _, _) when Value =< 16#3fff -> ++parse_settings_payload(<< 5:16, Value:32, _/bits >>, _, _) when Value =< 16#3fff -> + {connection_error, protocol_error, 'The SETTINGS_MAX_FRAME_SIZE value must be > 0x3fff. (RFC7540 6.5.2)'}; +-parse_settings(<< 5:16, Value:32, Rest/bits >>, Len, Settings) when Value =< 16#ffffff -> +- parse_settings(Rest, Len - 6, Settings#{max_frame_size => Value}); +-parse_settings(<< 5:16, _:32, _/bits >>, _, _) -> ++parse_settings_payload(<< 5:16, Value:32, Rest/bits >>, Len, Settings) when Value =< 16#ffffff -> ++ parse_settings_payload(Rest, Len - 6, Settings#{max_frame_size => Value}); ++parse_settings_payload(<< 5:16, _:32, _/bits >>, _, _) -> + {connection_error, protocol_error, 'The SETTINGS_MAX_FRAME_SIZE value must be =< 0xffffff. (RFC7540 6.5.2)'}; + %% SETTINGS_MAX_HEADER_LIST_SIZE. +-parse_settings(<< 6:16, Value:32, Rest/bits >>, Len, Settings) -> +- parse_settings(Rest, Len - 6, Settings#{max_header_list_size => Value}); +-parse_settings(<< _:48, Rest/bits >>, Len, Settings) -> +- parse_settings(Rest, Len - 6, Settings). ++parse_settings_payload(<< 6:16, Value:32, Rest/bits >>, Len, Settings) -> ++ parse_settings_payload(Rest, Len - 6, Settings#{max_header_list_size => Value}); ++parse_settings_payload(<< _:48, Rest/bits >>, Len, Settings) -> ++ parse_settings_payload(Rest, Len - 6, Settings). + + %% Building. + +diff --git a/src/cow_http_hd.erl b/src/cow_http_hd.erl +index e6c4524..c85d1fb 100644 +--- a/src/cow_http_hd.erl ++++ b/src/cow_http_hd.erl +@@ -1889,7 +1889,7 @@ horse_parse_host_ipv6_v4() -> + + -spec parse_http2_settings(binary()) -> binary(). + parse_http2_settings(HTTP2Settings) -> +- base64:decode(HTTP2Settings). ++ cow_http2:parse_settings_payload(base64:decode(HTTP2Settings)). + + %% @doc Parse the If-Match header. + diff --git a/erlang-cowlib.spec b/erlang-cowlib.spec new file mode 100644 index 0000000..bbe48d6 --- /dev/null +++ b/erlang-cowlib.spec @@ -0,0 +1,68 @@ +%global realname cowlib +%global upstream ninenines +# Technically, we're noarch; but erlang whose directories we install into is not. +%global debug_package %{nil} + + +Name: erlang-%{realname} +Version: 1.3.0 +Release: 1%{?dist} +Summary: Support library for manipulating Web protocols +Group: Development/Libraries +License: ASL 2.0 +URL: https://github.com/%{upstream}/%{realname} +%if 0%{?el7}%{?fedora} +VCS: scm:git:https://github.com/%{upstream}/%{realname}.git +%endif +Source0: https://github.com/%{upstream}/%{realname}/archive/%{version}/%{realname}-%{version}.tar.gz +Patch1: erlang-cowlib-0001-Add-HPACK-decoding-and-encoding-functions.patch +Patch2: erlang-cowlib-0002-WS-accepts-iodata-so-byte_size-won-t-work-unless-we-.patch +Patch3: erlang-cowlib-0003-Add-cowboy_http2-with-initial-HTTP-2-suppport.patch +Patch4: erlang-cowlib-0004-Fix-handling-of-default-values-in-cookie-options.patch +Patch5: erlang-cowlib-0005-Fix-a-few-binary-pattern-matching-issues.patch +Patch6: erlang-cowlib-0006-Add-cow_http-response-3.patch +Patch7: erlang-cowlib-0007-Add-settings_payload-1-settings_ack-0-and-ping-1-in-.patch +Patch8: erlang-cowlib-0008-Add-cow_http_hd-parse_http2_settings-1.patch +Patch9: erlang-cowlib-0009-Parse-the-settings-payload-directly-in-cow_http_hd-p.patch +BuildRequires: erlang-rebar +BuildRequires: erlang-triq + + +%description +Support library for manipulating Web protocols. + + +%prep +%setup -q -n %{realname}-%{version} +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 + + +%build +%{erlang_compile} + + +%install +%{erlang_install} + + +%check +%{__rebar} qc -vv + + +%files +%license LICENSE +%doc AUTHORS CHANGELOG.md README.md +%{erlang_appdir}/ + + +%changelog +* Mon Mar 14 2016 Peter Lemenkov - 1.3.0-1 +- Initial packaging diff --git a/sources b/sources index e69de29..5cb4aec 100644 --- a/sources +++ b/sources @@ -0,0 +1 @@ +78e79985dfd274dc980a9eae8f5bc33c cowlib-1.3.0.tar.gz