update
This commit is contained in:
5
phoenix/deps/hpax/.formatter.exs
Normal file
5
phoenix/deps/hpax/.formatter.exs
Normal file
@@ -0,0 +1,5 @@
|
||||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
|
||||
import_deps: [:stream_data]
|
||||
]
|
||||
BIN
phoenix/deps/hpax/.hex
Normal file
BIN
phoenix/deps/hpax/.hex
Normal file
Binary file not shown.
34
phoenix/deps/hpax/CHANGELOG.md
Normal file
34
phoenix/deps/hpax/CHANGELOG.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Changelog
|
||||
|
||||
## v1.0.3
|
||||
|
||||
* Silence warnings on (upcoming, at this time) Elixir 1.19+.
|
||||
|
||||
## v1.0.2
|
||||
|
||||
* The changes in v1.0.1 introduced some subtle compression errors with HPACK encoding. This has been fixed in this version. See [this issue](https://github.com/elixir-mint/hpax/issues/20) for more details.
|
||||
|
||||
## v1.0.1
|
||||
|
||||
* Fix some issues with dynamic table resizing. You should not need to do anything to your code, it should Just Work™. If you want to read more, [this issue](https://github.com/elixir-mint/hpax/issues/18) has all the context.
|
||||
|
||||
## v1.0.0
|
||||
|
||||
* Silence warnings on Elixir 1.17+.
|
||||
* Require Elixir 1.12+.
|
||||
|
||||
## v0.2.0
|
||||
|
||||
* Add `HPAX.new/2`, which supports a list of options. For now, the only option
|
||||
is `:huffman_encoding`, to choose whether to use Huffman encoding or not.
|
||||
* Add `HPAX.encode/3`, which supports encoding all headers with the same
|
||||
action.
|
||||
* Add the `HPAX.table/0` opaque type.
|
||||
|
||||
## v0.1.2
|
||||
|
||||
* Fix `use Bitwise` deprecation warning.
|
||||
|
||||
## v0.1.1
|
||||
|
||||
* Improve checking of dynamic resize updates.
|
||||
176
phoenix/deps/hpax/LICENSE.txt
Normal file
176
phoenix/deps/hpax/LICENSE.txt
Normal file
@@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
74
phoenix/deps/hpax/README.md
Normal file
74
phoenix/deps/hpax/README.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# HPAX
|
||||
|
||||

|
||||
[](https://hexdocs.pm/hpax)
|
||||
[](https://hex.pm/packages/hpax)
|
||||
[](https://coveralls.io/github/elixir-mint/hpax?branch=main)
|
||||
|
||||
HPAX is an Elixir implementation of the HPACK header compression algorithm as used in HTTP/2 and
|
||||
defined in RFC 7541. HPAX is used by several Elixir projects, including the
|
||||
[Mint](https://github.com/elixir-mint/mint) HTTP client and
|
||||
[bandit](https://github.com/mtrudel/bandit) HTTP server projects.
|
||||
|
||||
## Installation
|
||||
|
||||
To install HPAX, add it to your `mix.exs` file.
|
||||
|
||||
```elixir
|
||||
defp deps do
|
||||
[
|
||||
{:hpax, "~> 0.1.0"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Then, run `$ mix deps.get`.
|
||||
|
||||
## Usage
|
||||
|
||||
HPAX is designed to be used in both encoding and decoding scenarios. In both cases, a context is
|
||||
used to maintain state internal to the HPACK algorithm. In the common use case of using HPAX
|
||||
within HTTP/2, this context is called a **table** and must be shared between any
|
||||
subsequent encoding/decoding calls within
|
||||
an endpoint. Note that the contexts used for encoding and decoding within HTTP/2 are completely
|
||||
distinct from one another, even though they are structurally identical.
|
||||
|
||||
To encode a set of headers into a binary with HPAX:
|
||||
|
||||
```elixir
|
||||
context = HPAX.new(4096)
|
||||
headers = [{:store, ":status", "201"}, {:store, "location", "http://example.com"}]
|
||||
{encoded_headers, context} = HPAX.encode(headers, context)
|
||||
#=> {iodata, updated_context}
|
||||
```
|
||||
|
||||
To decode a binary into a set of headers with HPAX:
|
||||
|
||||
```elixir
|
||||
context = HPAX.new(4096)
|
||||
encoded_headers = <<...>>
|
||||
{:ok, headers, context} = HPAX.decode(encoded_headers, context)
|
||||
#=> {:ok, [{:store, ":status", "201"}, {:store, "location", "http://example.com"}], updated_context}
|
||||
```
|
||||
|
||||
For complete usage information, please see the HPAX [documentation](https://hex.pm/packages/hpax).
|
||||
|
||||
## Contributing
|
||||
|
||||
If you wish to contribute check out the [issue list](https://github.com/elixir-mint/hpax/issues) and let us know what you want to work on so we can discuss it and reduce duplicate work.
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2021 Eric Meadows-Jönsson and Andrea Leopardi
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
15
phoenix/deps/hpax/hex_metadata.config
Normal file
15
phoenix/deps/hpax/hex_metadata.config
Normal file
@@ -0,0 +1,15 @@
|
||||
{<<"links">>,[{<<"GitHub">>,<<"https://github.com/elixir-mint/hpax">>}]}.
|
||||
{<<"name">>,<<"hpax">>}.
|
||||
{<<"version">>,<<"1.0.3">>}.
|
||||
{<<"description">>,
|
||||
<<"Implementation of the HPACK protocol (RFC 7541) for Elixir">>}.
|
||||
{<<"elixir">>,<<"~> 1.12">>}.
|
||||
{<<"app">>,<<"hpax">>}.
|
||||
{<<"licenses">>,[<<"Apache-2.0">>]}.
|
||||
{<<"requirements">>,[]}.
|
||||
{<<"files">>,
|
||||
[<<"lib">>,<<"lib/hpax">>,<<"lib/hpax/huffman_table">>,
|
||||
<<"lib/hpax/types.ex">>,<<"lib/hpax/huffman.ex">>,<<"lib/hpax/table.ex">>,
|
||||
<<"lib/hpax.ex">>,<<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,
|
||||
<<"LICENSE.txt">>,<<"CHANGELOG.md">>]}.
|
||||
{<<"build_tools">>,[<<"mix">>]}.
|
||||
360
phoenix/deps/hpax/lib/hpax.ex
Normal file
360
phoenix/deps/hpax/lib/hpax.ex
Normal file
@@ -0,0 +1,360 @@
|
||||
defmodule HPAX do
|
||||
@moduledoc """
|
||||
Support for the HPACK header compression algorithm.
|
||||
|
||||
This module provides support for the HPACK header compression algorithm used mainly in HTTP/2.
|
||||
|
||||
## Encoding and decoding contexts
|
||||
|
||||
The HPACK algorithm requires both
|
||||
|
||||
* an encoding context on the encoder side
|
||||
* a decoding context on the decoder side
|
||||
|
||||
These contexts are semantically different but structurally the same. In HPACK they are
|
||||
implemented as **HPACK tables**. This library uses the name "tables" everywhere internally
|
||||
|
||||
HPACK tables can be created through the `new/1` function.
|
||||
"""
|
||||
|
||||
alias HPAX.{Table, Types}
|
||||
|
||||
@typedoc """
|
||||
An HPACK table.
|
||||
|
||||
This can be used for encoding or decoding.
|
||||
"""
|
||||
@typedoc since: "0.2.0"
|
||||
@opaque table() :: Table.t()
|
||||
|
||||
@typedoc """
|
||||
An HPACK header name.
|
||||
"""
|
||||
@type header_name() :: binary()
|
||||
|
||||
@typedoc """
|
||||
An HPACK header value.
|
||||
"""
|
||||
@type header_value() :: binary()
|
||||
|
||||
@valid_header_actions [:store, :store_name, :no_store, :never_store]
|
||||
|
||||
@doc """
|
||||
Creates a new HPACK table.
|
||||
|
||||
Same as `new/2` with default options.
|
||||
"""
|
||||
@spec new(non_neg_integer()) :: table()
|
||||
def new(max_table_size), do: new(max_table_size, [])
|
||||
|
||||
@doc """
|
||||
Create a new HPACK table that can be used as encoding or decoding context.
|
||||
|
||||
See the "Encoding and decoding contexts" section in the module documentation.
|
||||
|
||||
`max_table_size` is the maximum table size (in bytes) for the newly created table.
|
||||
|
||||
## Options
|
||||
|
||||
This function accepts the following `options`:
|
||||
|
||||
* `:huffman_encoding` - (since 0.2.0) `:always` or `:never`. If `:always`,
|
||||
then HPAX will always encode headers using Huffman encoding. If `:never`,
|
||||
HPAX will not use any Huffman encoding. Defaults to `:never`.
|
||||
|
||||
## Examples
|
||||
|
||||
encoding_context = HPAX.new(4096)
|
||||
|
||||
"""
|
||||
@doc since: "0.2.0"
|
||||
@spec new(non_neg_integer(), [keyword()]) :: table()
|
||||
def new(max_table_size, options)
|
||||
when is_integer(max_table_size) and max_table_size >= 0 and is_list(options) do
|
||||
options = Keyword.put_new(options, :huffman_encoding, :never)
|
||||
|
||||
Enum.each(options, fn
|
||||
{:huffman_encoding, _huffman_encoding} -> :ok
|
||||
{key, _value} -> raise ArgumentError, "unknown option: #{inspect(key)}"
|
||||
end)
|
||||
|
||||
Table.new(max_table_size, Keyword.fetch!(options, :huffman_encoding))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Resizes the given table to the given maximum size.
|
||||
|
||||
This is intended for use where the overlying protocol has signaled a change to the table's
|
||||
maximum size, such as when an HTTP/2 `SETTINGS` frame is received.
|
||||
|
||||
If the indicated size is less than the table's current size, entries
|
||||
will be evicted as needed to fit within the specified size, and the table's
|
||||
maximum size will be decreased to the specified value. A flag will also be
|
||||
set which will enqueue a "dynamic table size update" command to be prefixed
|
||||
to the next block encoded with this table, per
|
||||
[RFC9113§4.3.1](https://www.rfc-editor.org/rfc/rfc9113.html#section-4.3.1).
|
||||
|
||||
If the indicated size is greater than or equal to the table's current max size, no entries are evicted
|
||||
and the table's maximum size changes to the specified value.
|
||||
|
||||
## Examples
|
||||
|
||||
decoding_context = HPAX.new(4096)
|
||||
HPAX.resize(decoding_context, 8192)
|
||||
|
||||
"""
|
||||
@spec resize(table(), non_neg_integer()) :: table()
|
||||
defdelegate resize(table, new_max_size), to: Table
|
||||
|
||||
@doc """
|
||||
Decodes a header block fragment (HBF) through a given table.
|
||||
|
||||
If decoding is successful, this function returns a `{:ok, headers, updated_table}` tuple where
|
||||
`headers` is a list of decoded headers, and `updated_table` is the updated table. If there's
|
||||
an error in decoding, this function returns `{:error, reason}`.
|
||||
|
||||
## Examples
|
||||
|
||||
decoding_context = HPAX.new(1000)
|
||||
hbf = get_hbf_from_somewhere()
|
||||
HPAX.decode(hbf, decoding_context)
|
||||
#=> {:ok, [{":method", "GET"}], decoding_context}
|
||||
|
||||
"""
|
||||
@spec decode(binary(), table()) ::
|
||||
{:ok, [{header_name(), header_value()}], table()} | {:error, term()}
|
||||
|
||||
# Dynamic resizes must occur only at the start of a block
|
||||
# https://datatracker.ietf.org/doc/html/rfc7541#section-4.2
|
||||
def decode(<<0b001::3, rest::bitstring>>, %Table{} = table) do
|
||||
{new_max_size, rest} = decode_integer(rest, 5)
|
||||
|
||||
# Dynamic resizes must be less than protocol max table size
|
||||
# https://datatracker.ietf.org/doc/html/rfc7541#section-6.3
|
||||
if new_max_size <= table.protocol_max_table_size do
|
||||
decode(rest, Table.dynamic_resize(table, new_max_size))
|
||||
else
|
||||
{:error, :protocol_error}
|
||||
end
|
||||
end
|
||||
|
||||
def decode(block, %Table{} = table) when is_binary(block) do
|
||||
decode_headers(block, table, _acc = [])
|
||||
catch
|
||||
:throw, {:hpax, error} -> {:error, error}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Encodes a list of headers through the given table.
|
||||
|
||||
Returns a two-element tuple where the first element is a binary representing the encoded headers
|
||||
and the second element is an updated table.
|
||||
|
||||
## Examples
|
||||
|
||||
headers = [{:store, ":authority", "https://example.com"}]
|
||||
encoding_context = HPAX.new(1000)
|
||||
HPAX.encode(headers, encoding_context)
|
||||
#=> {iodata, updated_encoding_context}
|
||||
|
||||
"""
|
||||
@spec encode([header], table()) :: {iodata(), table()}
|
||||
when header: {action, header_name(), header_value()},
|
||||
action: :store | :store_name | :no_store | :never_store
|
||||
def encode(headers, %Table{} = table) when is_list(headers) do
|
||||
{table, pending_resizes} = Table.pop_pending_resizes(table)
|
||||
acc = Enum.map(pending_resizes, &[<<0b001::3, Types.encode_integer(&1, 5)::bitstring>>])
|
||||
encode_headers(headers, table, acc)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Encodes a list of headers through the given table, applying the same `action` to all of them.
|
||||
|
||||
This function is the similar to `encode/2`, but `headers` are `{name, value}` tuples instead,
|
||||
and the same `action` is applied to all headers.
|
||||
|
||||
## Examples
|
||||
|
||||
headers = [{":authority", "https://example.com"}]
|
||||
encoding_context = HPAX.new(1000)
|
||||
HPAX.encode(:store, headers, encoding_context)
|
||||
#=> {iodata, updated_encoding_context}
|
||||
|
||||
"""
|
||||
@doc since: "0.2.0"
|
||||
@spec encode(action, [header], table()) :: {iodata(), table()}
|
||||
when action: :store | :store_name | :no_store | :never_store,
|
||||
header: {header_name(), header_value()}
|
||||
def encode(action, headers, %Table{} = table)
|
||||
when is_list(headers) and action in [:store, :store_name, :no_store, :never_store] do
|
||||
headers
|
||||
|> Enum.map(fn {name, value} -> {action, name, value} end)
|
||||
|> encode(table)
|
||||
end
|
||||
|
||||
## Helpers
|
||||
|
||||
defp decode_headers(<<>>, table, acc) do
|
||||
{:ok, Enum.reverse(acc), table}
|
||||
end
|
||||
|
||||
# Indexed header field
|
||||
# http://httpwg.org/specs/rfc7541.html#rfc.section.6.1
|
||||
defp decode_headers(<<0b1::1, rest::bitstring>>, table, acc) do
|
||||
{index, rest} = decode_integer(rest, 7)
|
||||
decode_headers(rest, table, [lookup_by_index!(table, index) | acc])
|
||||
end
|
||||
|
||||
# Literal header field with incremental indexing
|
||||
# http://httpwg.org/specs/rfc7541.html#rfc.section.6.2.1
|
||||
defp decode_headers(<<0b01::2, rest::bitstring>>, table, acc) do
|
||||
{name, value, rest} =
|
||||
case rest do
|
||||
# The header name is a string.
|
||||
<<0::6, rest::binary>> ->
|
||||
{name, rest} = decode_binary(rest)
|
||||
{value, rest} = decode_binary(rest)
|
||||
{name, value, rest}
|
||||
|
||||
# The header name is an index to be looked up in the table.
|
||||
_other ->
|
||||
{index, rest} = decode_integer(rest, 6)
|
||||
{value, rest} = decode_binary(rest)
|
||||
{name, _value} = lookup_by_index!(table, index)
|
||||
{name, value, rest}
|
||||
end
|
||||
|
||||
decode_headers(rest, Table.add(table, name, value), [{name, value} | acc])
|
||||
end
|
||||
|
||||
# Literal header field without indexing
|
||||
# http://httpwg.org/specs/rfc7541.html#rfc.section.6.2.2
|
||||
defp decode_headers(<<0b0000::4, rest::bitstring>>, table, acc) do
|
||||
{name, value, rest} =
|
||||
case rest do
|
||||
<<0::4, rest::binary>> ->
|
||||
{name, rest} = decode_binary(rest)
|
||||
{value, rest} = decode_binary(rest)
|
||||
{name, value, rest}
|
||||
|
||||
_other ->
|
||||
{index, rest} = decode_integer(rest, 4)
|
||||
{value, rest} = decode_binary(rest)
|
||||
{name, _value} = lookup_by_index!(table, index)
|
||||
{name, value, rest}
|
||||
end
|
||||
|
||||
decode_headers(rest, table, [{name, value} | acc])
|
||||
end
|
||||
|
||||
# Literal header field never indexed
|
||||
# http://httpwg.org/specs/rfc7541.html#rfc.section.6.2.3
|
||||
defp decode_headers(<<0b0001::4, rest::bitstring>>, table, acc) do
|
||||
{name, value, rest} =
|
||||
case rest do
|
||||
<<0::4, rest::binary>> ->
|
||||
{name, rest} = decode_binary(rest)
|
||||
{value, rest} = decode_binary(rest)
|
||||
{name, value, rest}
|
||||
|
||||
_other ->
|
||||
{index, rest} = decode_integer(rest, 4)
|
||||
{value, rest} = decode_binary(rest)
|
||||
{name, _value} = lookup_by_index!(table, index)
|
||||
{name, value, rest}
|
||||
end
|
||||
|
||||
# TODO: enforce the "never indexed" part somehow.
|
||||
decode_headers(rest, table, [{name, value} | acc])
|
||||
end
|
||||
|
||||
defp decode_headers(_other, _table, _acc) do
|
||||
throw({:hpax, :protocol_error})
|
||||
end
|
||||
|
||||
defp lookup_by_index!(table, index) do
|
||||
case Table.lookup_by_index(table, index) do
|
||||
{:ok, header} -> header
|
||||
:error -> throw({:hpax, {:index_not_found, index}})
|
||||
end
|
||||
end
|
||||
|
||||
defp decode_integer(bitstring, prefix) do
|
||||
case Types.decode_integer(bitstring, prefix) do
|
||||
{:ok, int, rest} -> {int, rest}
|
||||
:error -> throw({:hpax, :bad_integer_encoding})
|
||||
end
|
||||
end
|
||||
|
||||
defp decode_binary(binary) do
|
||||
case Types.decode_binary(binary) do
|
||||
{:ok, binary, rest} -> {binary, rest}
|
||||
:error -> throw({:hpax, :bad_binary_encoding})
|
||||
end
|
||||
end
|
||||
|
||||
defp encode_headers([], table, acc) do
|
||||
{acc, table}
|
||||
end
|
||||
|
||||
defp encode_headers([{action, name, value} | rest], table, acc)
|
||||
when action in @valid_header_actions and is_binary(name) and is_binary(value) do
|
||||
huffman? = table.huffman_encoding == :always
|
||||
|
||||
{encoded, table} =
|
||||
case Table.lookup_by_header(table, name, value) do
|
||||
{:full, index} ->
|
||||
{encode_indexed_header(index), table}
|
||||
|
||||
{:name, index} when action == :store ->
|
||||
{encode_literal_header_with_indexing(index, value, huffman?),
|
||||
Table.add(table, name, value)}
|
||||
|
||||
{:name, index} when action in [:store_name, :no_store] ->
|
||||
{encode_literal_header_without_indexing(index, value, huffman?), table}
|
||||
|
||||
{:name, index} when action == :never_store ->
|
||||
{encode_literal_header_never_indexed(index, value, huffman?), table}
|
||||
|
||||
:not_found when action in [:store, :store_name] ->
|
||||
{encode_literal_header_with_indexing(name, value, huffman?),
|
||||
Table.add(table, name, value)}
|
||||
|
||||
:not_found when action == :no_store ->
|
||||
{encode_literal_header_without_indexing(name, value, huffman?), table}
|
||||
|
||||
:not_found when action == :never_store ->
|
||||
{encode_literal_header_never_indexed(name, value, huffman?), table}
|
||||
end
|
||||
|
||||
encode_headers(rest, table, [acc, encoded])
|
||||
end
|
||||
|
||||
defp encode_indexed_header(index) do
|
||||
<<1::1, Types.encode_integer(index, 7)::bitstring>>
|
||||
end
|
||||
|
||||
defp encode_literal_header_with_indexing(index, value, huffman?) when is_integer(index) do
|
||||
[<<1::2, Types.encode_integer(index, 6)::bitstring>>, Types.encode_binary(value, huffman?)]
|
||||
end
|
||||
|
||||
defp encode_literal_header_with_indexing(name, value, huffman?) when is_binary(name) do
|
||||
[<<1::2, 0::6>>, Types.encode_binary(name, huffman?), Types.encode_binary(value, huffman?)]
|
||||
end
|
||||
|
||||
defp encode_literal_header_without_indexing(index, value, huffman?) when is_integer(index) do
|
||||
[<<0::4, Types.encode_integer(index, 4)::bitstring>>, Types.encode_binary(value, huffman?)]
|
||||
end
|
||||
|
||||
defp encode_literal_header_without_indexing(name, value, huffman?) when is_binary(name) do
|
||||
[<<0::4, 0::4>>, Types.encode_binary(name, huffman?), Types.encode_binary(value, huffman?)]
|
||||
end
|
||||
|
||||
defp encode_literal_header_never_indexed(index, value, huffman?) when is_integer(index) do
|
||||
[<<1::4, Types.encode_integer(index, 4)::bitstring>>, Types.encode_binary(value, huffman?)]
|
||||
end
|
||||
|
||||
defp encode_literal_header_never_indexed(name, value, huffman?) when is_binary(name) do
|
||||
[<<1::4, 0::4>>, Types.encode_binary(name, huffman?), Types.encode_binary(value, huffman?)]
|
||||
end
|
||||
end
|
||||
94
phoenix/deps/hpax/lib/hpax/huffman.ex
Normal file
94
phoenix/deps/hpax/lib/hpax/huffman.ex
Normal file
@@ -0,0 +1,94 @@
|
||||
defmodule HPAX.Huffman do
|
||||
@moduledoc false
|
||||
|
||||
import Bitwise, only: [>>>: 2]
|
||||
|
||||
# This file is downloaded from the spec directly.
|
||||
# http://httpwg.org/specs/rfc7541.html#huffman.code
|
||||
table_file = Path.absname("huffman_table", __DIR__)
|
||||
@external_resource table_file
|
||||
|
||||
entries =
|
||||
Enum.map(File.stream!(table_file), fn line ->
|
||||
[byte_value, bits, _hex, bit_count] =
|
||||
line
|
||||
|> case do
|
||||
<<?', _, ?', ?\s, rest::binary>> -> rest
|
||||
"EOS " <> rest -> rest
|
||||
_other -> line
|
||||
end
|
||||
|> String.replace(["|", "(", ")", "[", "]"], "")
|
||||
|> String.split()
|
||||
|
||||
byte_value = String.to_integer(byte_value)
|
||||
bits = String.to_integer(bits, 2)
|
||||
bit_count = String.to_integer(bit_count)
|
||||
|
||||
{byte_value, bits, bit_count}
|
||||
end)
|
||||
|
||||
{regular_entries, [eos_entry]} = Enum.split(entries, -1)
|
||||
{_eos_byte_value, eos_bits, eos_bit_count} = eos_entry
|
||||
|
||||
## Encoding
|
||||
|
||||
@spec encode(binary()) :: binary()
|
||||
def encode(binary) do
|
||||
encode(binary, _acc = <<>>)
|
||||
end
|
||||
|
||||
for {byte_value, bits, bit_count} <- regular_entries do
|
||||
defp encode(<<unquote(byte_value), rest::binary>>, acc) do
|
||||
encode(rest, <<acc::bitstring, unquote(bits)::size(unquote(bit_count))>>)
|
||||
end
|
||||
end
|
||||
|
||||
defp encode(<<>>, acc) do
|
||||
overflowing_bits = rem(bit_size(acc), 8)
|
||||
|
||||
if overflowing_bits == 0 do
|
||||
acc
|
||||
else
|
||||
bits_to_add = 8 - overflowing_bits
|
||||
|
||||
value_of_bits_to_add =
|
||||
take_significant_bits(unquote(eos_bits), unquote(eos_bit_count), bits_to_add)
|
||||
|
||||
<<acc::bitstring, value_of_bits_to_add::size(bits_to_add)>>
|
||||
end
|
||||
end
|
||||
|
||||
## Decoding
|
||||
|
||||
@spec decode(binary()) :: binary()
|
||||
def decode(binary)
|
||||
|
||||
for {byte_value, bits, bit_count} <- regular_entries do
|
||||
def decode(<<unquote(bits)::size(unquote(bit_count)), rest::bitstring>>) do
|
||||
<<unquote(byte_value), decode(rest)::binary>>
|
||||
end
|
||||
end
|
||||
|
||||
def decode(<<>>) do
|
||||
<<>>
|
||||
end
|
||||
|
||||
# Use binary syntax for single match context optimization.
|
||||
def decode(<<padding::bitstring>>) when bit_size(padding) in 1..7 do
|
||||
padding_size = bit_size(padding)
|
||||
<<padding::size(padding_size)>> = padding
|
||||
|
||||
if take_significant_bits(unquote(eos_bits), unquote(eos_bit_count), padding_size) == padding do
|
||||
<<>>
|
||||
else
|
||||
throw({:hpax, {:protocol_error, :invalid_huffman_encoding}})
|
||||
end
|
||||
end
|
||||
|
||||
## Helpers
|
||||
|
||||
@compile {:inline, take_significant_bits: 3}
|
||||
defp take_significant_bits(value, bit_count, bits_to_take) do
|
||||
value >>> (bit_count - bits_to_take)
|
||||
end
|
||||
end
|
||||
257
phoenix/deps/hpax/lib/hpax/huffman_table
Normal file
257
phoenix/deps/hpax/lib/hpax/huffman_table
Normal file
@@ -0,0 +1,257 @@
|
||||
( 0) |11111111|11000 1ff8 [13]
|
||||
( 1) |11111111|11111111|1011000 7fffd8 [23]
|
||||
( 2) |11111111|11111111|11111110|0010 fffffe2 [28]
|
||||
( 3) |11111111|11111111|11111110|0011 fffffe3 [28]
|
||||
( 4) |11111111|11111111|11111110|0100 fffffe4 [28]
|
||||
( 5) |11111111|11111111|11111110|0101 fffffe5 [28]
|
||||
( 6) |11111111|11111111|11111110|0110 fffffe6 [28]
|
||||
( 7) |11111111|11111111|11111110|0111 fffffe7 [28]
|
||||
( 8) |11111111|11111111|11111110|1000 fffffe8 [28]
|
||||
( 9) |11111111|11111111|11101010 ffffea [24]
|
||||
( 10) |11111111|11111111|11111111|111100 3ffffffc [30]
|
||||
( 11) |11111111|11111111|11111110|1001 fffffe9 [28]
|
||||
( 12) |11111111|11111111|11111110|1010 fffffea [28]
|
||||
( 13) |11111111|11111111|11111111|111101 3ffffffd [30]
|
||||
( 14) |11111111|11111111|11111110|1011 fffffeb [28]
|
||||
( 15) |11111111|11111111|11111110|1100 fffffec [28]
|
||||
( 16) |11111111|11111111|11111110|1101 fffffed [28]
|
||||
( 17) |11111111|11111111|11111110|1110 fffffee [28]
|
||||
( 18) |11111111|11111111|11111110|1111 fffffef [28]
|
||||
( 19) |11111111|11111111|11111111|0000 ffffff0 [28]
|
||||
( 20) |11111111|11111111|11111111|0001 ffffff1 [28]
|
||||
( 21) |11111111|11111111|11111111|0010 ffffff2 [28]
|
||||
( 22) |11111111|11111111|11111111|111110 3ffffffe [30]
|
||||
( 23) |11111111|11111111|11111111|0011 ffffff3 [28]
|
||||
( 24) |11111111|11111111|11111111|0100 ffffff4 [28]
|
||||
( 25) |11111111|11111111|11111111|0101 ffffff5 [28]
|
||||
( 26) |11111111|11111111|11111111|0110 ffffff6 [28]
|
||||
( 27) |11111111|11111111|11111111|0111 ffffff7 [28]
|
||||
( 28) |11111111|11111111|11111111|1000 ffffff8 [28]
|
||||
( 29) |11111111|11111111|11111111|1001 ffffff9 [28]
|
||||
( 30) |11111111|11111111|11111111|1010 ffffffa [28]
|
||||
( 31) |11111111|11111111|11111111|1011 ffffffb [28]
|
||||
' ' ( 32) |010100 14 [ 6]
|
||||
'!' ( 33) |11111110|00 3f8 [10]
|
||||
'"' ( 34) |11111110|01 3f9 [10]
|
||||
'#' ( 35) |11111111|1010 ffa [12]
|
||||
'$' ( 36) |11111111|11001 1ff9 [13]
|
||||
'%' ( 37) |010101 15 [ 6]
|
||||
'&' ( 38) |11111000 f8 [ 8]
|
||||
''' ( 39) |11111111|010 7fa [11]
|
||||
'(' ( 40) |11111110|10 3fa [10]
|
||||
')' ( 41) |11111110|11 3fb [10]
|
||||
'*' ( 42) |11111001 f9 [ 8]
|
||||
'+' ( 43) |11111111|011 7fb [11]
|
||||
',' ( 44) |11111010 fa [ 8]
|
||||
'-' ( 45) |010110 16 [ 6]
|
||||
'.' ( 46) |010111 17 [ 6]
|
||||
'/' ( 47) |011000 18 [ 6]
|
||||
'0' ( 48) |00000 0 [ 5]
|
||||
'1' ( 49) |00001 1 [ 5]
|
||||
'2' ( 50) |00010 2 [ 5]
|
||||
'3' ( 51) |011001 19 [ 6]
|
||||
'4' ( 52) |011010 1a [ 6]
|
||||
'5' ( 53) |011011 1b [ 6]
|
||||
'6' ( 54) |011100 1c [ 6]
|
||||
'7' ( 55) |011101 1d [ 6]
|
||||
'8' ( 56) |011110 1e [ 6]
|
||||
'9' ( 57) |011111 1f [ 6]
|
||||
':' ( 58) |1011100 5c [ 7]
|
||||
';' ( 59) |11111011 fb [ 8]
|
||||
'<' ( 60) |11111111|1111100 7ffc [15]
|
||||
'=' ( 61) |100000 20 [ 6]
|
||||
'>' ( 62) |11111111|1011 ffb [12]
|
||||
'?' ( 63) |11111111|00 3fc [10]
|
||||
'@' ( 64) |11111111|11010 1ffa [13]
|
||||
'A' ( 65) |100001 21 [ 6]
|
||||
'B' ( 66) |1011101 5d [ 7]
|
||||
'C' ( 67) |1011110 5e [ 7]
|
||||
'D' ( 68) |1011111 5f [ 7]
|
||||
'E' ( 69) |1100000 60 [ 7]
|
||||
'F' ( 70) |1100001 61 [ 7]
|
||||
'G' ( 71) |1100010 62 [ 7]
|
||||
'H' ( 72) |1100011 63 [ 7]
|
||||
'I' ( 73) |1100100 64 [ 7]
|
||||
'J' ( 74) |1100101 65 [ 7]
|
||||
'K' ( 75) |1100110 66 [ 7]
|
||||
'L' ( 76) |1100111 67 [ 7]
|
||||
'M' ( 77) |1101000 68 [ 7]
|
||||
'N' ( 78) |1101001 69 [ 7]
|
||||
'O' ( 79) |1101010 6a [ 7]
|
||||
'P' ( 80) |1101011 6b [ 7]
|
||||
'Q' ( 81) |1101100 6c [ 7]
|
||||
'R' ( 82) |1101101 6d [ 7]
|
||||
'S' ( 83) |1101110 6e [ 7]
|
||||
'T' ( 84) |1101111 6f [ 7]
|
||||
'U' ( 85) |1110000 70 [ 7]
|
||||
'V' ( 86) |1110001 71 [ 7]
|
||||
'W' ( 87) |1110010 72 [ 7]
|
||||
'X' ( 88) |11111100 fc [ 8]
|
||||
'Y' ( 89) |1110011 73 [ 7]
|
||||
'Z' ( 90) |11111101 fd [ 8]
|
||||
'[' ( 91) |11111111|11011 1ffb [13]
|
||||
'\' ( 92) |11111111|11111110|000 7fff0 [19]
|
||||
']' ( 93) |11111111|11100 1ffc [13]
|
||||
'^' ( 94) |11111111|111100 3ffc [14]
|
||||
'_' ( 95) |100010 22 [ 6]
|
||||
'`' ( 96) |11111111|1111101 7ffd [15]
|
||||
'a' ( 97) |00011 3 [ 5]
|
||||
'b' ( 98) |100011 23 [ 6]
|
||||
'c' ( 99) |00100 4 [ 5]
|
||||
'd' (100) |100100 24 [ 6]
|
||||
'e' (101) |00101 5 [ 5]
|
||||
'f' (102) |100101 25 [ 6]
|
||||
'g' (103) |100110 26 [ 6]
|
||||
'h' (104) |100111 27 [ 6]
|
||||
'i' (105) |00110 6 [ 5]
|
||||
'j' (106) |1110100 74 [ 7]
|
||||
'k' (107) |1110101 75 [ 7]
|
||||
'l' (108) |101000 28 [ 6]
|
||||
'm' (109) |101001 29 [ 6]
|
||||
'n' (110) |101010 2a [ 6]
|
||||
'o' (111) |00111 7 [ 5]
|
||||
'p' (112) |101011 2b [ 6]
|
||||
'q' (113) |1110110 76 [ 7]
|
||||
'r' (114) |101100 2c [ 6]
|
||||
's' (115) |01000 8 [ 5]
|
||||
't' (116) |01001 9 [ 5]
|
||||
'u' (117) |101101 2d [ 6]
|
||||
'v' (118) |1110111 77 [ 7]
|
||||
'w' (119) |1111000 78 [ 7]
|
||||
'x' (120) |1111001 79 [ 7]
|
||||
'y' (121) |1111010 7a [ 7]
|
||||
'z' (122) |1111011 7b [ 7]
|
||||
'{' (123) |11111111|1111110 7ffe [15]
|
||||
'|' (124) |11111111|100 7fc [11]
|
||||
'}' (125) |11111111|111101 3ffd [14]
|
||||
'~' (126) |11111111|11101 1ffd [13]
|
||||
(127) |11111111|11111111|11111111|1100 ffffffc [28]
|
||||
(128) |11111111|11111110|0110 fffe6 [20]
|
||||
(129) |11111111|11111111|010010 3fffd2 [22]
|
||||
(130) |11111111|11111110|0111 fffe7 [20]
|
||||
(131) |11111111|11111110|1000 fffe8 [20]
|
||||
(132) |11111111|11111111|010011 3fffd3 [22]
|
||||
(133) |11111111|11111111|010100 3fffd4 [22]
|
||||
(134) |11111111|11111111|010101 3fffd5 [22]
|
||||
(135) |11111111|11111111|1011001 7fffd9 [23]
|
||||
(136) |11111111|11111111|010110 3fffd6 [22]
|
||||
(137) |11111111|11111111|1011010 7fffda [23]
|
||||
(138) |11111111|11111111|1011011 7fffdb [23]
|
||||
(139) |11111111|11111111|1011100 7fffdc [23]
|
||||
(140) |11111111|11111111|1011101 7fffdd [23]
|
||||
(141) |11111111|11111111|1011110 7fffde [23]
|
||||
(142) |11111111|11111111|11101011 ffffeb [24]
|
||||
(143) |11111111|11111111|1011111 7fffdf [23]
|
||||
(144) |11111111|11111111|11101100 ffffec [24]
|
||||
(145) |11111111|11111111|11101101 ffffed [24]
|
||||
(146) |11111111|11111111|010111 3fffd7 [22]
|
||||
(147) |11111111|11111111|1100000 7fffe0 [23]
|
||||
(148) |11111111|11111111|11101110 ffffee [24]
|
||||
(149) |11111111|11111111|1100001 7fffe1 [23]
|
||||
(150) |11111111|11111111|1100010 7fffe2 [23]
|
||||
(151) |11111111|11111111|1100011 7fffe3 [23]
|
||||
(152) |11111111|11111111|1100100 7fffe4 [23]
|
||||
(153) |11111111|11111110|11100 1fffdc [21]
|
||||
(154) |11111111|11111111|011000 3fffd8 [22]
|
||||
(155) |11111111|11111111|1100101 7fffe5 [23]
|
||||
(156) |11111111|11111111|011001 3fffd9 [22]
|
||||
(157) |11111111|11111111|1100110 7fffe6 [23]
|
||||
(158) |11111111|11111111|1100111 7fffe7 [23]
|
||||
(159) |11111111|11111111|11101111 ffffef [24]
|
||||
(160) |11111111|11111111|011010 3fffda [22]
|
||||
(161) |11111111|11111110|11101 1fffdd [21]
|
||||
(162) |11111111|11111110|1001 fffe9 [20]
|
||||
(163) |11111111|11111111|011011 3fffdb [22]
|
||||
(164) |11111111|11111111|011100 3fffdc [22]
|
||||
(165) |11111111|11111111|1101000 7fffe8 [23]
|
||||
(166) |11111111|11111111|1101001 7fffe9 [23]
|
||||
(167) |11111111|11111110|11110 1fffde [21]
|
||||
(168) |11111111|11111111|1101010 7fffea [23]
|
||||
(169) |11111111|11111111|011101 3fffdd [22]
|
||||
(170) |11111111|11111111|011110 3fffde [22]
|
||||
(171) |11111111|11111111|11110000 fffff0 [24]
|
||||
(172) |11111111|11111110|11111 1fffdf [21]
|
||||
(173) |11111111|11111111|011111 3fffdf [22]
|
||||
(174) |11111111|11111111|1101011 7fffeb [23]
|
||||
(175) |11111111|11111111|1101100 7fffec [23]
|
||||
(176) |11111111|11111111|00000 1fffe0 [21]
|
||||
(177) |11111111|11111111|00001 1fffe1 [21]
|
||||
(178) |11111111|11111111|100000 3fffe0 [22]
|
||||
(179) |11111111|11111111|00010 1fffe2 [21]
|
||||
(180) |11111111|11111111|1101101 7fffed [23]
|
||||
(181) |11111111|11111111|100001 3fffe1 [22]
|
||||
(182) |11111111|11111111|1101110 7fffee [23]
|
||||
(183) |11111111|11111111|1101111 7fffef [23]
|
||||
(184) |11111111|11111110|1010 fffea [20]
|
||||
(185) |11111111|11111111|100010 3fffe2 [22]
|
||||
(186) |11111111|11111111|100011 3fffe3 [22]
|
||||
(187) |11111111|11111111|100100 3fffe4 [22]
|
||||
(188) |11111111|11111111|1110000 7ffff0 [23]
|
||||
(189) |11111111|11111111|100101 3fffe5 [22]
|
||||
(190) |11111111|11111111|100110 3fffe6 [22]
|
||||
(191) |11111111|11111111|1110001 7ffff1 [23]
|
||||
(192) |11111111|11111111|11111000|00 3ffffe0 [26]
|
||||
(193) |11111111|11111111|11111000|01 3ffffe1 [26]
|
||||
(194) |11111111|11111110|1011 fffeb [20]
|
||||
(195) |11111111|11111110|001 7fff1 [19]
|
||||
(196) |11111111|11111111|100111 3fffe7 [22]
|
||||
(197) |11111111|11111111|1110010 7ffff2 [23]
|
||||
(198) |11111111|11111111|101000 3fffe8 [22]
|
||||
(199) |11111111|11111111|11110110|0 1ffffec [25]
|
||||
(200) |11111111|11111111|11111000|10 3ffffe2 [26]
|
||||
(201) |11111111|11111111|11111000|11 3ffffe3 [26]
|
||||
(202) |11111111|11111111|11111001|00 3ffffe4 [26]
|
||||
(203) |11111111|11111111|11111011|110 7ffffde [27]
|
||||
(204) |11111111|11111111|11111011|111 7ffffdf [27]
|
||||
(205) |11111111|11111111|11111001|01 3ffffe5 [26]
|
||||
(206) |11111111|11111111|11110001 fffff1 [24]
|
||||
(207) |11111111|11111111|11110110|1 1ffffed [25]
|
||||
(208) |11111111|11111110|010 7fff2 [19]
|
||||
(209) |11111111|11111111|00011 1fffe3 [21]
|
||||
(210) |11111111|11111111|11111001|10 3ffffe6 [26]
|
||||
(211) |11111111|11111111|11111100|000 7ffffe0 [27]
|
||||
(212) |11111111|11111111|11111100|001 7ffffe1 [27]
|
||||
(213) |11111111|11111111|11111001|11 3ffffe7 [26]
|
||||
(214) |11111111|11111111|11111100|010 7ffffe2 [27]
|
||||
(215) |11111111|11111111|11110010 fffff2 [24]
|
||||
(216) |11111111|11111111|00100 1fffe4 [21]
|
||||
(217) |11111111|11111111|00101 1fffe5 [21]
|
||||
(218) |11111111|11111111|11111010|00 3ffffe8 [26]
|
||||
(219) |11111111|11111111|11111010|01 3ffffe9 [26]
|
||||
(220) |11111111|11111111|11111111|1101 ffffffd [28]
|
||||
(221) |11111111|11111111|11111100|011 7ffffe3 [27]
|
||||
(222) |11111111|11111111|11111100|100 7ffffe4 [27]
|
||||
(223) |11111111|11111111|11111100|101 7ffffe5 [27]
|
||||
(224) |11111111|11111110|1100 fffec [20]
|
||||
(225) |11111111|11111111|11110011 fffff3 [24]
|
||||
(226) |11111111|11111110|1101 fffed [20]
|
||||
(227) |11111111|11111111|00110 1fffe6 [21]
|
||||
(228) |11111111|11111111|101001 3fffe9 [22]
|
||||
(229) |11111111|11111111|00111 1fffe7 [21]
|
||||
(230) |11111111|11111111|01000 1fffe8 [21]
|
||||
(231) |11111111|11111111|1110011 7ffff3 [23]
|
||||
(232) |11111111|11111111|101010 3fffea [22]
|
||||
(233) |11111111|11111111|101011 3fffeb [22]
|
||||
(234) |11111111|11111111|11110111|0 1ffffee [25]
|
||||
(235) |11111111|11111111|11110111|1 1ffffef [25]
|
||||
(236) |11111111|11111111|11110100 fffff4 [24]
|
||||
(237) |11111111|11111111|11110101 fffff5 [24]
|
||||
(238) |11111111|11111111|11111010|10 3ffffea [26]
|
||||
(239) |11111111|11111111|1110100 7ffff4 [23]
|
||||
(240) |11111111|11111111|11111010|11 3ffffeb [26]
|
||||
(241) |11111111|11111111|11111100|110 7ffffe6 [27]
|
||||
(242) |11111111|11111111|11111011|00 3ffffec [26]
|
||||
(243) |11111111|11111111|11111011|01 3ffffed [26]
|
||||
(244) |11111111|11111111|11111100|111 7ffffe7 [27]
|
||||
(245) |11111111|11111111|11111101|000 7ffffe8 [27]
|
||||
(246) |11111111|11111111|11111101|001 7ffffe9 [27]
|
||||
(247) |11111111|11111111|11111101|010 7ffffea [27]
|
||||
(248) |11111111|11111111|11111101|011 7ffffeb [27]
|
||||
(249) |11111111|11111111|11111111|1110 ffffffe [28]
|
||||
(250) |11111111|11111111|11111101|100 7ffffec [27]
|
||||
(251) |11111111|11111111|11111101|101 7ffffed [27]
|
||||
(252) |11111111|11111111|11111101|110 7ffffee [27]
|
||||
(253) |11111111|11111111|11111101|111 7ffffef [27]
|
||||
(254) |11111111|11111111|11111110|000 7fffff0 [27]
|
||||
(255) |11111111|11111111|11111011|10 3ffffee [26]
|
||||
EOS (256) |11111111|11111111|11111111|111111 3fffffff [30]
|
||||
348
phoenix/deps/hpax/lib/hpax/table.ex
Normal file
348
phoenix/deps/hpax/lib/hpax/table.ex
Normal file
@@ -0,0 +1,348 @@
|
||||
defmodule HPAX.Table do
|
||||
@moduledoc false
|
||||
|
||||
@enforce_keys [:max_table_size, :huffman_encoding]
|
||||
defstruct [
|
||||
:protocol_max_table_size,
|
||||
:max_table_size,
|
||||
:huffman_encoding,
|
||||
entries: [],
|
||||
size: 0,
|
||||
length: 0,
|
||||
pending_minimum_resize: nil
|
||||
]
|
||||
|
||||
@type huffman_encoding() :: :always | :never
|
||||
|
||||
@type t() :: %__MODULE__{
|
||||
protocol_max_table_size: non_neg_integer(),
|
||||
max_table_size: non_neg_integer(),
|
||||
huffman_encoding: huffman_encoding(),
|
||||
entries: [{binary(), binary()}],
|
||||
size: non_neg_integer(),
|
||||
length: non_neg_integer(),
|
||||
pending_minimum_resize: non_neg_integer() | nil
|
||||
}
|
||||
|
||||
@static_table [
|
||||
{":authority", nil},
|
||||
{":method", "GET"},
|
||||
{":method", "POST"},
|
||||
{":path", "/"},
|
||||
{":path", "/index.html"},
|
||||
{":scheme", "http"},
|
||||
{":scheme", "https"},
|
||||
{":status", "200"},
|
||||
{":status", "204"},
|
||||
{":status", "206"},
|
||||
{":status", "304"},
|
||||
{":status", "400"},
|
||||
{":status", "404"},
|
||||
{":status", "500"},
|
||||
{"accept-charset", nil},
|
||||
{"accept-encoding", "gzip, deflate"},
|
||||
{"accept-language", nil},
|
||||
{"accept-ranges", nil},
|
||||
{"accept", nil},
|
||||
{"access-control-allow-origin", nil},
|
||||
{"age", nil},
|
||||
{"allow", nil},
|
||||
{"authorization", nil},
|
||||
{"cache-control", nil},
|
||||
{"content-disposition", nil},
|
||||
{"content-encoding", nil},
|
||||
{"content-language", nil},
|
||||
{"content-length", nil},
|
||||
{"content-location", nil},
|
||||
{"content-range", nil},
|
||||
{"content-type", nil},
|
||||
{"cookie", nil},
|
||||
{"date", nil},
|
||||
{"etag", nil},
|
||||
{"expect", nil},
|
||||
{"expires", nil},
|
||||
{"from", nil},
|
||||
{"host", nil},
|
||||
{"if-match", nil},
|
||||
{"if-modified-since", nil},
|
||||
{"if-none-match", nil},
|
||||
{"if-range", nil},
|
||||
{"if-unmodified-since", nil},
|
||||
{"last-modified", nil},
|
||||
{"link", nil},
|
||||
{"location", nil},
|
||||
{"max-forwards", nil},
|
||||
{"proxy-authenticate", nil},
|
||||
{"proxy-authorization", nil},
|
||||
{"range", nil},
|
||||
{"referer", nil},
|
||||
{"refresh", nil},
|
||||
{"retry-after", nil},
|
||||
{"server", nil},
|
||||
{"set-cookie", nil},
|
||||
{"strict-transport-security", nil},
|
||||
{"transfer-encoding", nil},
|
||||
{"user-agent", nil},
|
||||
{"vary", nil},
|
||||
{"via", nil},
|
||||
{"www-authenticate", nil}
|
||||
]
|
||||
|
||||
@static_table_size length(@static_table)
|
||||
@dynamic_table_start @static_table_size + 1
|
||||
|
||||
@doc """
|
||||
Creates a new HPACK table with the given maximum size.
|
||||
|
||||
The maximum size is not the maximum number of entries but rather the maximum size as defined in
|
||||
http://httpwg.org/specs/rfc7541.html#maximum.table.size.
|
||||
"""
|
||||
@spec new(non_neg_integer(), huffman_encoding()) :: t()
|
||||
def new(protocol_max_table_size, huffman_encoding)
|
||||
when is_integer(protocol_max_table_size) and protocol_max_table_size >= 0 and
|
||||
huffman_encoding in [:always, :never] do
|
||||
%__MODULE__{
|
||||
protocol_max_table_size: protocol_max_table_size,
|
||||
max_table_size: protocol_max_table_size,
|
||||
huffman_encoding: huffman_encoding
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Adds the given header to the given table.
|
||||
|
||||
If the new entry does not fit within the max table size then the oldest entries will be evicted.
|
||||
|
||||
Header names should be lowercase when added to the HPACK table
|
||||
as per the [HTTP/2 spec](https://http2.github.io/http2-spec/#rfc.section.8.1.2):
|
||||
|
||||
> header field names MUST be converted to lowercase prior to their encoding in HTTP/2
|
||||
|
||||
"""
|
||||
@spec add(t(), binary(), binary()) :: t()
|
||||
def add(%__MODULE__{} = table, name, value) do
|
||||
%{max_table_size: max_table_size, size: size} = table
|
||||
entry_size = entry_size(name, value)
|
||||
|
||||
cond do
|
||||
# An attempt to add an entry larger than the maximum size causes the table to be emptied of
|
||||
# all existing entries and results in an empty table.
|
||||
entry_size > max_table_size ->
|
||||
%{table | entries: [], size: 0, length: 0}
|
||||
|
||||
size + entry_size > max_table_size ->
|
||||
table
|
||||
|> evict_to_size(max_table_size - entry_size)
|
||||
|> add_header(name, value, entry_size)
|
||||
|
||||
true ->
|
||||
add_header(table, name, value, entry_size)
|
||||
end
|
||||
end
|
||||
|
||||
defp add_header(%__MODULE__{} = table, name, value, entry_size) do
|
||||
%{entries: entries, size: size, length: length} = table
|
||||
%{table | entries: [{name, value} | entries], size: size + entry_size, length: length + 1}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Looks up a header by index `index` in the given `table`.
|
||||
|
||||
Returns `{:ok, {name, value}}` if a header is found at the given `index`, otherwise returns
|
||||
`:error`. `value` can be a binary in case both the header name and value are present in the
|
||||
table, or `nil` if only the name is present (this can only happen in the static table).
|
||||
"""
|
||||
@spec lookup_by_index(t(), pos_integer()) :: {:ok, {binary(), binary() | nil}} | :error
|
||||
def lookup_by_index(table, index)
|
||||
|
||||
# Static table
|
||||
for {header, index} <- Enum.with_index(@static_table, 1) do
|
||||
def lookup_by_index(%__MODULE__{}, unquote(index)), do: {:ok, unquote(header)}
|
||||
end
|
||||
|
||||
def lookup_by_index(%__MODULE__{length: 0}, _index) do
|
||||
:error
|
||||
end
|
||||
|
||||
def lookup_by_index(%__MODULE__{entries: entries, length: length}, index)
|
||||
when index >= @dynamic_table_start and index <= @dynamic_table_start + length - 1 do
|
||||
{:ok, Enum.at(entries, index - @dynamic_table_start)}
|
||||
end
|
||||
|
||||
def lookup_by_index(%__MODULE__{}, _index) do
|
||||
:error
|
||||
end
|
||||
|
||||
@doc """
|
||||
Looks up the index of a header by its name and value.
|
||||
|
||||
It returns:
|
||||
|
||||
* `{:full, index}` if the full header (name and value) are present in the table at `index`
|
||||
|
||||
* `{:name, index}` if `name` is present in the table but with a different value than `value`
|
||||
|
||||
* `:not_found` if the header name is not in the table at all
|
||||
|
||||
Header names should be lowercase when looked up in the HPACK table
|
||||
as per the [HTTP/2 spec](https://http2.github.io/http2-spec/#rfc.section.8.1.2):
|
||||
|
||||
> header field names MUST be converted to lowercase prior to their encoding in HTTP/2
|
||||
|
||||
"""
|
||||
@spec lookup_by_header(t(), binary(), binary() | nil) ::
|
||||
{:full, pos_integer()} | {:name, pos_integer()} | :not_found
|
||||
def lookup_by_header(table, name, value)
|
||||
|
||||
def lookup_by_header(%__MODULE__{entries: entries}, name, value) do
|
||||
case static_lookup_by_header(name, value) do
|
||||
{:full, _index} = result ->
|
||||
result
|
||||
|
||||
{:name, index} ->
|
||||
# Check if we get full match in the dynamic tabble
|
||||
case dynamic_lookup_by_header(entries, name, value, @dynamic_table_start, nil) do
|
||||
{:full, _index} = result -> result
|
||||
_other -> {:name, index}
|
||||
end
|
||||
|
||||
:not_found ->
|
||||
dynamic_lookup_by_header(entries, name, value, @dynamic_table_start, nil)
|
||||
end
|
||||
end
|
||||
|
||||
for {{name, value}, index} when is_binary(value) <- Enum.with_index(@static_table, 1) do
|
||||
defp static_lookup_by_header(unquote(name), unquote(value)) do
|
||||
{:full, unquote(index)}
|
||||
end
|
||||
end
|
||||
|
||||
static_table_names =
|
||||
@static_table
|
||||
|> Enum.map(&elem(&1, 0))
|
||||
|> Enum.with_index(1)
|
||||
|> Enum.uniq_by(&elem(&1, 0))
|
||||
|
||||
for {name, index} <- static_table_names do
|
||||
defp static_lookup_by_header(unquote(name), _value) do
|
||||
{:name, unquote(index)}
|
||||
end
|
||||
end
|
||||
|
||||
defp static_lookup_by_header(_name, _value) do
|
||||
:not_found
|
||||
end
|
||||
|
||||
defp dynamic_lookup_by_header([{name, value} | _rest], name, value, index, _name_index) do
|
||||
{:full, index}
|
||||
end
|
||||
|
||||
defp dynamic_lookup_by_header([{name, _} | rest], name, value, index, _name_index) do
|
||||
dynamic_lookup_by_header(rest, name, value, index + 1, index)
|
||||
end
|
||||
|
||||
defp dynamic_lookup_by_header([_other | rest], name, value, index, name_index) do
|
||||
dynamic_lookup_by_header(rest, name, value, index + 1, name_index)
|
||||
end
|
||||
|
||||
defp dynamic_lookup_by_header([], _name, _value, _index, name_index) do
|
||||
if name_index, do: {:name, name_index}, else: :not_found
|
||||
end
|
||||
|
||||
@doc """
|
||||
Changes the table's protocol negotiated maximum size, possibly evicting entries as needed to satisfy.
|
||||
|
||||
If the indicated size is less than the table's current max size, entries
|
||||
will be evicted as needed to fit within the specified size, and the table's
|
||||
maximum size will be decreased to the specified value. An will also be
|
||||
set which will enqueue a 'dynamic table size update' command to be prefixed
|
||||
to the next block encoded with this table, per RFC9113§4.3.1.
|
||||
|
||||
If the indicated size is greater than or equal to the table's current max size, no entries are evicted
|
||||
and the table's maximum size changes to the specified value.
|
||||
|
||||
In all cases, the table's `:protocol_max_table_size` is updated accordingly
|
||||
"""
|
||||
@spec resize(t(), non_neg_integer()) :: t()
|
||||
def resize(%__MODULE__{} = table, new_protocol_max_table_size) do
|
||||
pending_minimum_resize =
|
||||
case table.pending_minimum_resize do
|
||||
nil -> new_protocol_max_table_size
|
||||
current -> min(current, new_protocol_max_table_size)
|
||||
end
|
||||
|
||||
%{
|
||||
evict_to_size(table, new_protocol_max_table_size)
|
||||
| protocol_max_table_size: new_protocol_max_table_size,
|
||||
max_table_size: new_protocol_max_table_size,
|
||||
pending_minimum_resize: pending_minimum_resize
|
||||
}
|
||||
end
|
||||
|
||||
def dynamic_resize(%__MODULE__{} = table, new_max_table_size) do
|
||||
%{
|
||||
evict_to_size(table, new_max_table_size)
|
||||
| max_table_size: new_max_table_size
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns (and clears) any pending resize events on the table which will need to be signalled to
|
||||
the decoder via dynamic table size update messages. Intended to be called at the start of any
|
||||
block encode to prepend such dynamic table size update(s) as needed. The value of
|
||||
`pending_minimum_resize` indicates the smallest maximum size of this table which has not yet
|
||||
been signalled to the decoder, and is always included in the list returned if it is set.
|
||||
Additionally, if the current max table size is larger than this value, it is also included int
|
||||
the list, per https://www.rfc-editor.org/rfc/rfc7541#section-4.2
|
||||
"""
|
||||
def pop_pending_resizes(%__MODULE__{pending_minimum_resize: nil} = table), do: {table, []}
|
||||
|
||||
def pop_pending_resizes(%__MODULE__{} = table) do
|
||||
pending_resizes =
|
||||
if table.max_table_size > table.pending_minimum_resize,
|
||||
do: [table.pending_minimum_resize, table.max_table_size],
|
||||
else: [table.pending_minimum_resize]
|
||||
|
||||
{%{table | pending_minimum_resize: nil}, pending_resizes}
|
||||
end
|
||||
|
||||
# Removes records as necessary to have the total size of entries within the table be less than
|
||||
# or equal to the specified value. Does not change the table's max size.
|
||||
defp evict_to_size(%__MODULE__{size: size} = table, new_size) when size <= new_size, do: table
|
||||
|
||||
defp evict_to_size(%__MODULE__{entries: entries, size: size} = table, new_size) do
|
||||
{new_entries_reversed, new_size} =
|
||||
evict_towards_size(Enum.reverse(entries), size, new_size)
|
||||
|
||||
%{
|
||||
table
|
||||
| entries: Enum.reverse(new_entries_reversed),
|
||||
size: new_size,
|
||||
length: length(new_entries_reversed)
|
||||
}
|
||||
end
|
||||
|
||||
defp evict_towards_size([{name, value} | rest], size, max_target_size) do
|
||||
new_size = size - entry_size(name, value)
|
||||
|
||||
if new_size <= max_target_size do
|
||||
{rest, new_size}
|
||||
else
|
||||
evict_towards_size(rest, new_size, max_target_size)
|
||||
end
|
||||
end
|
||||
|
||||
defp evict_towards_size([], 0, _max_target_size) do
|
||||
{[], 0}
|
||||
end
|
||||
|
||||
defp entry_size(name, value) do
|
||||
byte_size(name) + byte_size(value) + 32
|
||||
end
|
||||
|
||||
# Made public to be used in tests.
|
||||
@doc false
|
||||
def __static_table__() do
|
||||
@static_table
|
||||
end
|
||||
end
|
||||
89
phoenix/deps/hpax/lib/hpax/types.ex
Normal file
89
phoenix/deps/hpax/lib/hpax/types.ex
Normal file
@@ -0,0 +1,89 @@
|
||||
defmodule HPAX.Types do
|
||||
@moduledoc false
|
||||
|
||||
import Bitwise, only: [<<<: 2]
|
||||
|
||||
alias HPAX.Huffman
|
||||
|
||||
# This is used as a macro and not an inlined function because we want to be able to use it in
|
||||
# guards.
|
||||
defmacrop power_of_two(n) do
|
||||
quote do: 1 <<< unquote(n)
|
||||
end
|
||||
|
||||
## Encoding
|
||||
|
||||
@spec encode_integer(non_neg_integer(), 1..8) :: bitstring()
|
||||
def encode_integer(integer, prefix)
|
||||
|
||||
def encode_integer(integer, prefix) when integer < power_of_two(prefix) - 1 do
|
||||
<<integer::size(prefix)>>
|
||||
end
|
||||
|
||||
def encode_integer(integer, prefix) do
|
||||
initial = power_of_two(prefix) - 1
|
||||
remaining = integer - initial
|
||||
<<initial::size(prefix), encode_remaining_integer(remaining)::binary>>
|
||||
end
|
||||
|
||||
defp encode_remaining_integer(remaining) when remaining >= 128 do
|
||||
first = rem(remaining, 128) + 128
|
||||
<<first::8, encode_remaining_integer(div(remaining, 128))::binary>>
|
||||
end
|
||||
|
||||
defp encode_remaining_integer(remaining) do
|
||||
<<remaining::8>>
|
||||
end
|
||||
|
||||
@spec encode_binary(binary(), boolean()) :: iodata()
|
||||
def encode_binary(binary, huffman?) do
|
||||
binary = if huffman?, do: Huffman.encode(binary), else: binary
|
||||
huffman_bit = if huffman?, do: 1, else: 0
|
||||
binary_size = encode_integer(byte_size(binary), 7)
|
||||
[<<huffman_bit::1, binary_size::bitstring>>, binary]
|
||||
end
|
||||
|
||||
## Decoding
|
||||
|
||||
@spec decode_integer(bitstring, 1..8) :: {:ok, non_neg_integer(), binary()} | :error
|
||||
def decode_integer(bitstring, prefix) when is_bitstring(bitstring) and prefix in 1..8 do
|
||||
with <<value::size(prefix), rest::binary>> <- bitstring do
|
||||
if value < power_of_two(prefix) - 1 do
|
||||
{:ok, value, rest}
|
||||
else
|
||||
decode_remaining_integer(rest, value, 0)
|
||||
end
|
||||
else
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
|
||||
defp decode_remaining_integer(<<0::1, value::7, rest::binary>>, int, m) do
|
||||
{:ok, int + (value <<< m), rest}
|
||||
end
|
||||
|
||||
defp decode_remaining_integer(<<1::1, value::7, rest::binary>>, int, m) do
|
||||
decode_remaining_integer(rest, int + (value <<< m), m + 7)
|
||||
end
|
||||
|
||||
defp decode_remaining_integer(_, _, _) do
|
||||
:error
|
||||
end
|
||||
|
||||
@spec decode_binary(binary) :: {:ok, binary(), binary()} | :error
|
||||
def decode_binary(binary) when is_binary(binary) do
|
||||
with <<huffman_bit::1, rest::bitstring>> <- binary,
|
||||
{:ok, length, rest} <- decode_integer(rest, 7),
|
||||
<<contents::binary-size(length), rest::binary>> <- rest do
|
||||
contents =
|
||||
case huffman_bit do
|
||||
0 -> contents
|
||||
1 -> Huffman.decode(contents)
|
||||
end
|
||||
|
||||
{:ok, contents, rest}
|
||||
else
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
end
|
||||
54
phoenix/deps/hpax/mix.exs
Normal file
54
phoenix/deps/hpax/mix.exs
Normal file
@@ -0,0 +1,54 @@
|
||||
defmodule HPAX.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
@version "1.0.3"
|
||||
@repo_url "https://github.com/elixir-mint/hpax"
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :hpax,
|
||||
version: @version,
|
||||
elixir: "~> 1.12",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
|
||||
# Tests
|
||||
test_coverage: [tool: ExCoveralls],
|
||||
|
||||
# Hex
|
||||
package: package(),
|
||||
description: "Implementation of the HPACK protocol (RFC 7541) for Elixir",
|
||||
|
||||
# Docs
|
||||
name: "HPAX",
|
||||
docs: [
|
||||
source_ref: "v#{@version}",
|
||||
source_url: @repo_url
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
def application do
|
||||
[
|
||||
extra_applications: []
|
||||
]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[
|
||||
{:ex_doc, "~> 0.34", only: :dev},
|
||||
{:hpack, ">= 0.0.0", hex: :hpack_erl, only: :test},
|
||||
{:stream_data, "~> 1.0", only: [:dev, :test]},
|
||||
{:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false},
|
||||
{:excoveralls, "~> 0.18", only: :test},
|
||||
{:castore, "~> 1.0", only: :test}
|
||||
]
|
||||
end
|
||||
|
||||
defp package do
|
||||
[
|
||||
licenses: ["Apache-2.0"],
|
||||
links: %{"GitHub" => @repo_url}
|
||||
]
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user