aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LICENSE13
-rw-r--r--Makefile5
-rw-r--r--README.md20
-rw-r--r--erlang.mk224
-rw-r--r--src/cow_spdy.erl264
-rw-r--r--src/cow_spdy.hrl181
-rw-r--r--src/cowlib.app.src24
7 files changed, 731 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3e227ee
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,13 @@
+Copyright (c) 2013, Loïc Hoguin <[email protected]>
+
+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.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3b44c01
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,5 @@
+# See LICENSE for licensing information.
+
+PROJECT = cowlib
+
+include erlang.mk
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9523836
--- /dev/null
+++ b/README.md
@@ -0,0 +1,20 @@
+Cowlib
+======
+
+Cowlib is a support library for manipulating Web protocols.
+
+Goals
+-----
+
+Cowlib provides libraries for parsing and building messages
+for various Web protocols, including SPDY, HTTP and Websocket.
+
+It is optimized for completeness rather than speed. No value
+is ignored, they are all returned.
+
+Support
+-------
+
+ * Official IRC Channel: #ninenines on irc.freenode.net
+ * [Mailing Lists](http://lists.ninenines.eu)
+ * [Commercial Support](http://ninenines.eu/support)
diff --git a/erlang.mk b/erlang.mk
new file mode 100644
index 0000000..f073155
--- /dev/null
+++ b/erlang.mk
@@ -0,0 +1,224 @@
+# Copyright (c) 2013, Loïc Hoguin <[email protected]>
+#
+# 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.
+
+# Project.
+
+PROJECT ?= $(notdir $(CURDIR))
+
+# Packages database file.
+
+PKG_FILE ?= $(CURDIR)/.erlang.mk.packages.v1
+export PKG_FILE
+
+PKG_FILE_URL ?= https://raw.github.com/extend/erlang.mk/master/packages.v1.txt
+
+define get_pkg_file
+ wget -O $(PKG_FILE) $(PKG_FILE_URL)
+endef
+
+# Verbosity and tweaks.
+
+V ?= 0
+
+appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src;
+appsrc_verbose = $(appsrc_verbose_$(V))
+
+erlc_verbose_0 = @echo " ERLC " $(filter %.erl %.core,$(?F));
+erlc_verbose = $(erlc_verbose_$(V))
+
+xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F));
+xyrl_verbose = $(xyrl_verbose_$(V))
+
+dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
+dtl_verbose = $(dtl_verbose_$(V))
+
+gen_verbose_0 = @echo " GEN " $@;
+gen_verbose = $(gen_verbose_$(V))
+
+.PHONY: all clean-all app clean deps clean-deps docs clean-docs \
+ build-tests tests build-plt dialyze
+
+# Deps directory.
+
+DEPS_DIR ?= $(CURDIR)/deps
+export DEPS_DIR
+
+REBAR_DEPS_DIR = $(DEPS_DIR)
+export REBAR_DEPS_DIR
+
+ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DEPS))
+ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS))
+
+# Application.
+
+ERLC_OPTS ?= -Werror +debug_info +warn_export_all +warn_export_vars \
+ +warn_shadow_vars +warn_obsolete_guard # +bin_opt_info +warn_missing_spec
+COMPILE_FIRST ?=
+COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST)))
+
+all: deps app
+
+clean-all: clean clean-deps clean-docs
+ $(gen_verbose) rm -rf .$(PROJECT).plt $(DEPS_DIR) logs
+
+app: ebin/$(PROJECT).app
+ $(eval MODULES := $(shell find ebin -name \*.beam \
+ | sed 's/ebin\///;s/\.beam/,/' | sed '$$s/.$$//'))
+ $(appsrc_verbose) cat src/$(PROJECT).app.src \
+ | sed 's/{modules, \[\]}/{modules, \[$(MODULES)\]}/' \
+ > ebin/$(PROJECT).app
+
+define compile_erl
+ $(erlc_verbose) ERL_LIBS=$(DEPS_DIR) erlc -v $(ERLC_OPTS) -o ebin/ \
+ -pa ebin/ -I include/ $(COMPILE_FIRST_PATHS) $(1)
+endef
+
+define compile_xyrl
+ $(xyrl_verbose) erlc -v -o ebin/ $(1)
+ $(xyrl_verbose) erlc $(ERLC_OPTS) -o ebin/ ebin/*.erl
+ @rm ebin/*.erl
+endef
+
+define compile_dtl
+ $(dtl_verbose) erl -noshell -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/ -eval ' \
+ Compile = fun(F) -> \
+ Module = list_to_atom( \
+ string:to_lower(filename:basename(F, ".dtl")) ++ "_dtl"), \
+ erlydtl_compiler:compile(F, Module, [{out_dir, "ebin/"}]) \
+ end, \
+ _ = [Compile(F) || F <- string:tokens("$(1)", " ")], \
+ init:stop()'
+endef
+
+ebin/$(PROJECT).app: src/*.erl $(wildcard src/*.core) \
+ $(wildcard src/*.xrl) $(wildcard src/*.yrl) \
+ $(wildcard templates/*.dtl)
+ @mkdir -p ebin/
+ $(if $(strip $(filter %.erl %.core,$?)), \
+ $(call compile_erl,$(filter %.erl %.core,$?)))
+ $(if $(strip $(filter %.xrl %.yrl,$?)), \
+ $(call compile_xyrl,$(filter %.xrl %.yrl,$?)))
+ $(if $(strip $(filter %.dtl,$?)), \
+ $(call compile_dtl,$(filter %.dtl,$?)))
+
+clean:
+ $(gen_verbose) rm -rf ebin/ test/*.beam erl_crash.dump
+
+# Dependencies.
+
+define get_dep
+ @mkdir -p $(DEPS_DIR)
+ifeq (,$(findstring pkg://,$(word 1,$(dep_$(1)))))
+ git clone -n -- $(word 1,$(dep_$(1))) $(DEPS_DIR)/$(1)
+else
+ @if [ ! -f $(PKG_FILE) ]; then $(call get_pkg_file); fi
+ git clone -n -- `awk 'BEGIN { FS = "\t" }; \
+ $$$$1 == "$(subst pkg://,,$(word 1,$(dep_$(1))))" { print $$$$2 }' \
+ $(PKG_FILE)` $(DEPS_DIR)/$(1)
+endif
+ cd $(DEPS_DIR)/$(1) ; git checkout -q $(word 2,$(dep_$(1)))
+endef
+
+define dep_target
+$(DEPS_DIR)/$(1):
+ $(call get_dep,$(1))
+endef
+
+$(foreach dep,$(DEPS),$(eval $(call dep_target,$(dep))))
+
+deps: $(ALL_DEPS_DIRS)
+ @for dep in $(ALL_DEPS_DIRS) ; do \
+ if [ -f $$dep/Makefile ] ; then \
+ $(MAKE) -C $$dep ; \
+ else \
+ echo "include $(CURDIR)/erlang.mk" | $(MAKE) -f - -C $$dep ; \
+ fi ; \
+ done
+
+clean-deps:
+ @for dep in $(ALL_DEPS_DIRS) ; do $(MAKE) -C $$dep clean; done
+
+# Documentation.
+
+docs: clean-docs
+ $(gen_verbose) erl -noshell \
+ -eval 'edoc:application($(PROJECT), ".", []), init:stop().'
+
+clean-docs:
+ $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
+
+# Tests.
+
+$(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep))))
+
+build-test-deps: $(ALL_TEST_DEPS_DIRS)
+ @for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
+
+build-tests: build-test-deps
+ $(gen_verbose) ERL_LIBS=$(DEPS_DIR) erlc -v $(ERLC_OPTS) -o test/ \
+ $(wildcard test/*.erl test/*/*.erl) -pa ebin/
+
+CT_RUN = ct_run \
+ -no_auto_compile \
+ -noshell \
+ -pa ebin $(DEPS_DIR)/*/ebin \
+ -dir test \
+ -logdir logs
+# -cover test/cover.spec
+
+CT_SUITES ?=
+CT_SUITES_FULL = $(addsuffix _SUITE,$(CT_SUITES))
+
+tests: ERLC_OPTS += -DTEST=1 +'{parse_transform, eunit_autoexport}'
+tests: clean deps app build-tests
+ @mkdir -p logs/
+ @$(CT_RUN) -suite $(CT_SUITES_FULL)
+ $(gen_verbose) rm -f test/*.beam
+
+# Dialyzer.
+
+PLT_APPS ?=
+DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions \
+ -Wunmatched_returns # -Wunderspecs
+
+build-plt: deps app
+ @dialyzer --build_plt --output_plt .$(PROJECT).plt \
+ --apps erts kernel stdlib $(PLT_APPS) $(ALL_DEPS_DIRS)
+
+dialyze:
+ @dialyzer --src src --plt .$(PROJECT).plt --no_native $(DIALYZER_OPTS)
+
+# Packages.
+
+$(PKG_FILE):
+ @$(call get_pkg_file)
+
+pkg-list: $(PKG_FILE)
+ @cat $(PKG_FILE) | awk 'BEGIN { FS = "\t" }; { print \
+ "Name:\t\t" $$1 "\n" \
+ "Repository:\t" $$2 "\n" \
+ "Website:\t" $$3 "\n" \
+ "Description:\t" $$4 "\n" }'
+
+ifdef q
+pkg-search: $(PKG_FILE)
+ @cat $(PKG_FILE) | grep -i ${q} | awk 'BEGIN { FS = "\t" }; { print \
+ "Name:\t\t" $$1 "\n" \
+ "Repository:\t" $$2 "\n" \
+ "Website:\t" $$3 "\n" \
+ "Description:\t" $$4 "\n" }'
+else
+pkg-search:
+ @echo "Usage: make pkg-search q=STRING"
+endif
diff --git a/src/cow_spdy.erl b/src/cow_spdy.erl
new file mode 100644
index 0000000..4856afd
--- /dev/null
+++ b/src/cow_spdy.erl
@@ -0,0 +1,264 @@
+%% Copyright (c) 2013, Loïc Hoguin <[email protected]>
+%%
+%% 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_spdy).
+
+%% Zstream.
+-export([deflate_init/0]).
+-export([inflate_init/0]).
+
+%% Parse.
+-export([split/1]).
+-export([parse/2]).
+
+%% Build.
+-export([data/3]).
+-export([syn_stream/12]).
+-export([syn_reply/6]).
+-export([rst_stream/2]).
+%% @todo settings
+-export([ping/1]).
+-export([goaway/2]).
+%% @todo headers
+%% @todo window_update
+
+-include("cow_spdy.hrl").
+
+%% Zstream.
+
+deflate_init() ->
+ Zdef = zlib:open(),
+ ok = zlib:deflateInit(Zdef),
+ _ = zlib:deflateSetDictionary(Zdef, ?ZDICT),
+ Zdef.
+
+inflate_init() ->
+ Zinf = zlib:open(),
+ ok = zlib:inflateInit(Zinf),
+ Zinf.
+
+%% Parse.
+
+split(Data = << _:40, Length:24, _/bits >>)
+ when byte_size(Data) >= Length + 8 ->
+ Length2 = Length + 8,
+ << Frame:Length2/binary, Rest/bits >> = Data,
+ {true, Frame, Rest};
+split(Data) ->
+ {false, Data}.
+
+parse(<< 0:1, StreamID:31, 0:7, IsFinFlag:1, _:24, Data/bits >>, _) ->
+ {data, StreamID, from_flag(IsFinFlag), Data};
+parse(<< 1:1, 3:15, 1:16, 0:6, IsUnidirectionalFlag:1, IsFinFlag:1,
+ _:25, StreamID:31, _:1, AssocToStreamID:31, Priority:3, _:5,
+ 0:8, Rest/bits >>, Zinf) ->
+ case parse_headers(Rest, Zinf) of
+ {ok, Headers, [{<<":host">>, Host}, {<<":method">>, Method},
+ {<<":path">>, Path}, {<<":scheme">>, Scheme},
+ {<<":version">>, Version}]} ->
+ {syn_stream, StreamID, AssocToStreamID, from_flag(IsFinFlag),
+ from_flag(IsUnidirectionalFlag), Priority, Method,
+ Scheme, Host, Path, Version, Headers};
+ _ ->
+ {error, badprotocol}
+ end;
+parse(<< 1:1, 3:15, 2:16, 0:7, IsFinFlag:1, _:25,
+ StreamID:31, Rest/bits >>, Zinf) ->
+ case parse_headers(Rest, Zinf) of
+ {ok, Headers, [{<<":status">>, Status}, {<<":version">>, Version}]} ->
+ {syn_reply, StreamID, from_flag(IsFinFlag),
+ Status, Version, Headers};
+ _ ->
+ {error, badprotocol}
+ end;
+parse(<< 1:1, 3:15, 3:16, 0:8, _:56, StatusCode:32 >>, _)
+ when StatusCode =:= 0; StatusCode > 11 ->
+ {error, badprotocol};
+parse(<< 1:1, 3:15, 3:16, 0:8, _:25, StreamID:31, StatusCode:32 >>, _) ->
+ Status = case StatusCode of
+ 1 -> protocol_error;
+ 2 -> invalid_stream;
+ 3 -> refused_stream;
+ 4 -> unsupported_version;
+ 5 -> cancel;
+ 6 -> internal_error;
+ 7 -> flow_control_error;
+ 8 -> stream_in_use;
+ 9 -> stream_already_closed;
+ 10 -> invalid_credentials;
+ 11 -> frame_too_large
+ end,
+ {rst_stream, StreamID, Status};
+parse(<< 1:1, 3:15, 4:16, 0:7, ClearSettingsFlag:1, _:24,
+ NbEntries:32, Rest/bits >>, _) ->
+ try
+ Settings = [begin
+ Is0 = 0,
+ Key = case ID of
+ 1 -> upload_bandwidth;
+ 2 -> download_bandwidth;
+ 3 -> round_trip_time;
+ 4 -> max_concurrent_streams;
+ 5 -> current_cwnd;
+ 6 -> download_retrans_rate;
+ 7 -> initial_window_size;
+ 8 -> client_certificate_vector_size
+ end,
+ {Key, Value, from_flag(PersistFlag), from_flag(WasPersistedFlag)}
+ end || << Is0:6, WasPersistedFlag:1, PersistFlag:1,
+ ID:24, Value:32 >> <= Rest],
+ NbEntries = length(Settings),
+ {settings, from_flag(ClearSettingsFlag), Settings}
+ catch _:_ ->
+ {error, badprotocol}
+ end;
+parse(<< 1:1, 3:15, 6:16, 0:8, _:24, PingID:32 >>, _) ->
+ {ping, PingID};
+parse(<< 1:1, 3:15, 7:16, 0:8, _:56, StatusCode:32 >>, _)
+ when StatusCode > 2 ->
+ {error, badprotocol};
+parse(<< 1:1, 3:15, 7:16, 0:8, _:25, LastGoodStreamID:31,
+ StatusCode:32 >>, _) ->
+ Status = case StatusCode of
+ 0 -> ok;
+ 1 -> protocol_error;
+ 2 -> internal_error
+ end,
+ {goaway, LastGoodStreamID, Status};
+parse(<< 1:1, 3:15, 8:16, 0:7, IsFinFlag:1, _:25, StreamID:31,
+ Rest/bits >>, Zinf) ->
+ case parse_headers(Rest, Zinf) of
+ {ok, Headers, []} ->
+ {headers, StreamID, from_flag(IsFinFlag), Headers};
+ _ ->
+ {error, badprotocol}
+ end;
+parse(<< 1:1, 3:15, 9:16, 0:8, _:57, 0:31 >>, _) ->
+ {error, badprotocol};
+parse(<< 1:1, 3:15, 9:16, 0:8, _:25, StreamID:31,
+ _:1, DeltaWindowSize:31 >>, _) ->
+ {window_update, StreamID, DeltaWindowSize};
+parse(_, _) ->
+ {error, badprotocol}.
+
+parse_headers(Data, Zinf) ->
+ [<< NbHeaders:32, Rest/bits >>] = inflate(Zinf, Data),
+ parse_headers(Rest, NbHeaders, [], []).
+
+parse_headers(<<>>, 0, Headers, SpHeaders) ->
+ {ok, lists:reverse(Headers), lists:sort(SpHeaders)};
+parse_headers(<<>>, _, _, _) ->
+ error;
+parse_headers(_, 0, _, _) ->
+ error;
+parse_headers(<< 0:32, _/bits >>, _, _, _) ->
+ error;
+parse_headers(<< L1:32, Key:L1/binary, L2:32, Value:L2/binary, Rest/bits >>,
+ NbHeaders, Acc, SpAcc) ->
+ case Key of
+ << $:, _/bits >> ->
+ parse_headers(Rest, NbHeaders - 1, Acc, [{Key, Value}|SpAcc]);
+ _ ->
+ parse_headers(Rest, NbHeaders - 1, [{Key, Value}|Acc], SpAcc)
+ end.
+
+inflate(Zinf, Data) ->
+ try
+ zlib:inflate(Zinf, Data)
+ catch _:_ ->
+ ok = zlib:inflateSetDictionary(Zinf, ?ZDICT),
+ zlib:inflate(Zinf, <<>>)
+ end.
+
+from_flag(0) -> false;
+from_flag(1) -> true.
+
+%% Build.
+
+data(StreamID, IsFin, Data) ->
+ IsFinFlag = to_flag(IsFin),
+ Length = iolist_size(Data),
+ [<< 0:1, StreamID:31, 0:7, IsFinFlag:1, Length:24 >>, Data].
+
+syn_stream(Zdef, StreamID, AssocToStreamID, IsFin, IsUnidirectional,
+ Priority, Method, Scheme, Host, Path, Version, Headers) ->
+ IsFinFlag = to_flag(IsFin),
+ IsUnidirectionalFlag = to_flag(IsUnidirectional),
+ HeaderBlock = build_headers(Zdef, [
+ {<<":method">>, Method},
+ {<<":scheme">>, Scheme},
+ {<<":host">>, Host},
+ {<<":path">>, Path},
+ {<<":version">>, Version}
+ |Headers]),
+ Length = 10 + iolist_size(HeaderBlock),
+ [<< 1:1, 3:15, 1:16, 0:6, IsUnidirectionalFlag:1, IsFinFlag:1,
+ Length:24, 0:1, StreamID:31, 0:1, AssocToStreamID:31,
+ Priority:3, 0:5, 0:8 >>, HeaderBlock].
+
+syn_reply(Zdef, StreamID, IsFin, Status, Version, Headers) ->
+ IsFinFlag = to_flag(IsFin),
+ HeaderBlock = build_headers(Zdef, [
+ {<<":status">>, Status},
+ {<<":version">>, Version}
+ |Headers]),
+ Length = 4 + iolist_size(HeaderBlock),
+ [<< 1:1, 3:15, 2:16, 0:7, IsFinFlag:1, Length:24,
+ 0:1, StreamID:31 >>, HeaderBlock].
+
+rst_stream(StreamID, Status) ->
+ StatusCode = case Status of
+ protocol_error -> 1;
+ invalid_stream -> 2;
+ refused_stream -> 3;
+ unsupported_version -> 4;
+ cancel -> 5;
+ internal_error -> 6;
+ flow_control_error -> 7;
+ stream_in_use -> 8;
+ stream_already_closed -> 9;
+ invalid_credentials -> 10;
+ frame_too_large -> 11
+ end,
+ << 1:1, 3:15, 3:16, 0:8, 8:24,
+ 0:1, StreamID:31, StatusCode:32 >>.
+
+%% @todo settings
+
+ping(PingID) ->
+ << 1:1, 3:15, 6:16, 0:8, 4:24, PingID:32 >>.
+
+goaway(LastGoodStreamID, Status) ->
+ StatusCode = case Status of
+ ok -> 0;
+ protocol_error -> 1;
+ internal_error -> 2
+ end,
+ << 1:1, 3:15, 7:16, 0:8, 8:24,
+ 0:1, LastGoodStreamID:31, StatusCode:32 >>.
+
+%% @todo headers
+%% @todo window_update
+
+build_headers(Zdef, Headers) ->
+ NbHeaders = length(Headers),
+ Headers2 = [begin
+ L1 = iolist_size(Key),
+ L2 = iolist_size(Value),
+ [<< L1:32 >>, Key, << L2:32 >>, Value]
+ end || {Key, Value} <- Headers],
+ zlib:deflate(Zdef, [<< NbHeaders:32 >>, Headers2], full).
+
+to_flag(false) -> 0;
+to_flag(true) -> 1.
diff --git a/src/cow_spdy.hrl b/src/cow_spdy.hrl
new file mode 100644
index 0000000..9637b1c
--- /dev/null
+++ b/src/cow_spdy.hrl
@@ -0,0 +1,181 @@
+%% Zlib dictionary.
+
+-define(ZDICT, <<
+ 16#00, 16#00, 16#00, 16#07, 16#6f, 16#70, 16#74, 16#69,
+ 16#6f, 16#6e, 16#73, 16#00, 16#00, 16#00, 16#04, 16#68,
+ 16#65, 16#61, 16#64, 16#00, 16#00, 16#00, 16#04, 16#70,
+ 16#6f, 16#73, 16#74, 16#00, 16#00, 16#00, 16#03, 16#70,
+ 16#75, 16#74, 16#00, 16#00, 16#00, 16#06, 16#64, 16#65,
+ 16#6c, 16#65, 16#74, 16#65, 16#00, 16#00, 16#00, 16#05,
+ 16#74, 16#72, 16#61, 16#63, 16#65, 16#00, 16#00, 16#00,
+ 16#06, 16#61, 16#63, 16#63, 16#65, 16#70, 16#74, 16#00,
+ 16#00, 16#00, 16#0e, 16#61, 16#63, 16#63, 16#65, 16#70,
+ 16#74, 16#2d, 16#63, 16#68, 16#61, 16#72, 16#73, 16#65,
+ 16#74, 16#00, 16#00, 16#00, 16#0f, 16#61, 16#63, 16#63,
+ 16#65, 16#70, 16#74, 16#2d, 16#65, 16#6e, 16#63, 16#6f,
+ 16#64, 16#69, 16#6e, 16#67, 16#00, 16#00, 16#00, 16#0f,
+ 16#61, 16#63, 16#63, 16#65, 16#70, 16#74, 16#2d, 16#6c,
+ 16#61, 16#6e, 16#67, 16#75, 16#61, 16#67, 16#65, 16#00,
+ 16#00, 16#00, 16#0d, 16#61, 16#63, 16#63, 16#65, 16#70,
+ 16#74, 16#2d, 16#72, 16#61, 16#6e, 16#67, 16#65, 16#73,
+ 16#00, 16#00, 16#00, 16#03, 16#61, 16#67, 16#65, 16#00,
+ 16#00, 16#00, 16#05, 16#61, 16#6c, 16#6c, 16#6f, 16#77,
+ 16#00, 16#00, 16#00, 16#0d, 16#61, 16#75, 16#74, 16#68,
+ 16#6f, 16#72, 16#69, 16#7a, 16#61, 16#74, 16#69, 16#6f,
+ 16#6e, 16#00, 16#00, 16#00, 16#0d, 16#63, 16#61, 16#63,
+ 16#68, 16#65, 16#2d, 16#63, 16#6f, 16#6e, 16#74, 16#72,
+ 16#6f, 16#6c, 16#00, 16#00, 16#00, 16#0a, 16#63, 16#6f,
+ 16#6e, 16#6e, 16#65, 16#63, 16#74, 16#69, 16#6f, 16#6e,
+ 16#00, 16#00, 16#00, 16#0c, 16#63, 16#6f, 16#6e, 16#74,
+ 16#65, 16#6e, 16#74, 16#2d, 16#62, 16#61, 16#73, 16#65,
+ 16#00, 16#00, 16#00, 16#10, 16#63, 16#6f, 16#6e, 16#74,
+ 16#65, 16#6e, 16#74, 16#2d, 16#65, 16#6e, 16#63, 16#6f,
+ 16#64, 16#69, 16#6e, 16#67, 16#00, 16#00, 16#00, 16#10,
+ 16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e, 16#74, 16#2d,
+ 16#6c, 16#61, 16#6e, 16#67, 16#75, 16#61, 16#67, 16#65,
+ 16#00, 16#00, 16#00, 16#0e, 16#63, 16#6f, 16#6e, 16#74,
+ 16#65, 16#6e, 16#74, 16#2d, 16#6c, 16#65, 16#6e, 16#67,
+ 16#74, 16#68, 16#00, 16#00, 16#00, 16#10, 16#63, 16#6f,
+ 16#6e, 16#74, 16#65, 16#6e, 16#74, 16#2d, 16#6c, 16#6f,
+ 16#63, 16#61, 16#74, 16#69, 16#6f, 16#6e, 16#00, 16#00,
+ 16#00, 16#0b, 16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e,
+ 16#74, 16#2d, 16#6d, 16#64, 16#35, 16#00, 16#00, 16#00,
+ 16#0d, 16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e, 16#74,
+ 16#2d, 16#72, 16#61, 16#6e, 16#67, 16#65, 16#00, 16#00,
+ 16#00, 16#0c, 16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e,
+ 16#74, 16#2d, 16#74, 16#79, 16#70, 16#65, 16#00, 16#00,
+ 16#00, 16#04, 16#64, 16#61, 16#74, 16#65, 16#00, 16#00,
+ 16#00, 16#04, 16#65, 16#74, 16#61, 16#67, 16#00, 16#00,
+ 16#00, 16#06, 16#65, 16#78, 16#70, 16#65, 16#63, 16#74,
+ 16#00, 16#00, 16#00, 16#07, 16#65, 16#78, 16#70, 16#69,
+ 16#72, 16#65, 16#73, 16#00, 16#00, 16#00, 16#04, 16#66,
+ 16#72, 16#6f, 16#6d, 16#00, 16#00, 16#00, 16#04, 16#68,
+ 16#6f, 16#73, 16#74, 16#00, 16#00, 16#00, 16#08, 16#69,
+ 16#66, 16#2d, 16#6d, 16#61, 16#74, 16#63, 16#68, 16#00,
+ 16#00, 16#00, 16#11, 16#69, 16#66, 16#2d, 16#6d, 16#6f,
+ 16#64, 16#69, 16#66, 16#69, 16#65, 16#64, 16#2d, 16#73,
+ 16#69, 16#6e, 16#63, 16#65, 16#00, 16#00, 16#00, 16#0d,
+ 16#69, 16#66, 16#2d, 16#6e, 16#6f, 16#6e, 16#65, 16#2d,
+ 16#6d, 16#61, 16#74, 16#63, 16#68, 16#00, 16#00, 16#00,
+ 16#08, 16#69, 16#66, 16#2d, 16#72, 16#61, 16#6e, 16#67,
+ 16#65, 16#00, 16#00, 16#00, 16#13, 16#69, 16#66, 16#2d,
+ 16#75, 16#6e, 16#6d, 16#6f, 16#64, 16#69, 16#66, 16#69,
+ 16#65, 16#64, 16#2d, 16#73, 16#69, 16#6e, 16#63, 16#65,
+ 16#00, 16#00, 16#00, 16#0d, 16#6c, 16#61, 16#73, 16#74,
+ 16#2d, 16#6d, 16#6f, 16#64, 16#69, 16#66, 16#69, 16#65,
+ 16#64, 16#00, 16#00, 16#00, 16#08, 16#6c, 16#6f, 16#63,
+ 16#61, 16#74, 16#69, 16#6f, 16#6e, 16#00, 16#00, 16#00,
+ 16#0c, 16#6d, 16#61, 16#78, 16#2d, 16#66, 16#6f, 16#72,
+ 16#77, 16#61, 16#72, 16#64, 16#73, 16#00, 16#00, 16#00,
+ 16#06, 16#70, 16#72, 16#61, 16#67, 16#6d, 16#61, 16#00,
+ 16#00, 16#00, 16#12, 16#70, 16#72, 16#6f, 16#78, 16#79,
+ 16#2d, 16#61, 16#75, 16#74, 16#68, 16#65, 16#6e, 16#74,
+ 16#69, 16#63, 16#61, 16#74, 16#65, 16#00, 16#00, 16#00,
+ 16#13, 16#70, 16#72, 16#6f, 16#78, 16#79, 16#2d, 16#61,
+ 16#75, 16#74, 16#68, 16#6f, 16#72, 16#69, 16#7a, 16#61,
+ 16#74, 16#69, 16#6f, 16#6e, 16#00, 16#00, 16#00, 16#05,
+ 16#72, 16#61, 16#6e, 16#67, 16#65, 16#00, 16#00, 16#00,
+ 16#07, 16#72, 16#65, 16#66, 16#65, 16#72, 16#65, 16#72,
+ 16#00, 16#00, 16#00, 16#0b, 16#72, 16#65, 16#74, 16#72,
+ 16#79, 16#2d, 16#61, 16#66, 16#74, 16#65, 16#72, 16#00,
+ 16#00, 16#00, 16#06, 16#73, 16#65, 16#72, 16#76, 16#65,
+ 16#72, 16#00, 16#00, 16#00, 16#02, 16#74, 16#65, 16#00,
+ 16#00, 16#00, 16#07, 16#74, 16#72, 16#61, 16#69, 16#6c,
+ 16#65, 16#72, 16#00, 16#00, 16#00, 16#11, 16#74, 16#72,
+ 16#61, 16#6e, 16#73, 16#66, 16#65, 16#72, 16#2d, 16#65,
+ 16#6e, 16#63, 16#6f, 16#64, 16#69, 16#6e, 16#67, 16#00,
+ 16#00, 16#00, 16#07, 16#75, 16#70, 16#67, 16#72, 16#61,
+ 16#64, 16#65, 16#00, 16#00, 16#00, 16#0a, 16#75, 16#73,
+ 16#65, 16#72, 16#2d, 16#61, 16#67, 16#65, 16#6e, 16#74,
+ 16#00, 16#00, 16#00, 16#04, 16#76, 16#61, 16#72, 16#79,
+ 16#00, 16#00, 16#00, 16#03, 16#76, 16#69, 16#61, 16#00,
+ 16#00, 16#00, 16#07, 16#77, 16#61, 16#72, 16#6e, 16#69,
+ 16#6e, 16#67, 16#00, 16#00, 16#00, 16#10, 16#77, 16#77,
+ 16#77, 16#2d, 16#61, 16#75, 16#74, 16#68, 16#65, 16#6e,
+ 16#74, 16#69, 16#63, 16#61, 16#74, 16#65, 16#00, 16#00,
+ 16#00, 16#06, 16#6d, 16#65, 16#74, 16#68, 16#6f, 16#64,
+ 16#00, 16#00, 16#00, 16#03, 16#67, 16#65, 16#74, 16#00,
+ 16#00, 16#00, 16#06, 16#73, 16#74, 16#61, 16#74, 16#75,
+ 16#73, 16#00, 16#00, 16#00, 16#06, 16#32, 16#30, 16#30,
+ 16#20, 16#4f, 16#4b, 16#00, 16#00, 16#00, 16#07, 16#76,
+ 16#65, 16#72, 16#73, 16#69, 16#6f, 16#6e, 16#00, 16#00,
+ 16#00, 16#08, 16#48, 16#54, 16#54, 16#50, 16#2f, 16#31,
+ 16#2e, 16#31, 16#00, 16#00, 16#00, 16#03, 16#75, 16#72,
+ 16#6c, 16#00, 16#00, 16#00, 16#06, 16#70, 16#75, 16#62,
+ 16#6c, 16#69, 16#63, 16#00, 16#00, 16#00, 16#0a, 16#73,
+ 16#65, 16#74, 16#2d, 16#63, 16#6f, 16#6f, 16#6b, 16#69,
+ 16#65, 16#00, 16#00, 16#00, 16#0a, 16#6b, 16#65, 16#65,
+ 16#70, 16#2d, 16#61, 16#6c, 16#69, 16#76, 16#65, 16#00,
+ 16#00, 16#00, 16#06, 16#6f, 16#72, 16#69, 16#67, 16#69,
+ 16#6e, 16#31, 16#30, 16#30, 16#31, 16#30, 16#31, 16#32,
+ 16#30, 16#31, 16#32, 16#30, 16#32, 16#32, 16#30, 16#35,
+ 16#32, 16#30, 16#36, 16#33, 16#30, 16#30, 16#33, 16#30,
+ 16#32, 16#33, 16#30, 16#33, 16#33, 16#30, 16#34, 16#33,
+ 16#30, 16#35, 16#33, 16#30, 16#36, 16#33, 16#30, 16#37,
+ 16#34, 16#30, 16#32, 16#34, 16#30, 16#35, 16#34, 16#30,
+ 16#36, 16#34, 16#30, 16#37, 16#34, 16#30, 16#38, 16#34,
+ 16#30, 16#39, 16#34, 16#31, 16#30, 16#34, 16#31, 16#31,
+ 16#34, 16#31, 16#32, 16#34, 16#31, 16#33, 16#34, 16#31,
+ 16#34, 16#34, 16#31, 16#35, 16#34, 16#31, 16#36, 16#34,
+ 16#31, 16#37, 16#35, 16#30, 16#32, 16#35, 16#30, 16#34,
+ 16#35, 16#30, 16#35, 16#32, 16#30, 16#33, 16#20, 16#4e,
+ 16#6f, 16#6e, 16#2d, 16#41, 16#75, 16#74, 16#68, 16#6f,
+ 16#72, 16#69, 16#74, 16#61, 16#74, 16#69, 16#76, 16#65,
+ 16#20, 16#49, 16#6e, 16#66, 16#6f, 16#72, 16#6d, 16#61,
+ 16#74, 16#69, 16#6f, 16#6e, 16#32, 16#30, 16#34, 16#20,
+ 16#4e, 16#6f, 16#20, 16#43, 16#6f, 16#6e, 16#74, 16#65,
+ 16#6e, 16#74, 16#33, 16#30, 16#31, 16#20, 16#4d, 16#6f,
+ 16#76, 16#65, 16#64, 16#20, 16#50, 16#65, 16#72, 16#6d,
+ 16#61, 16#6e, 16#65, 16#6e, 16#74, 16#6c, 16#79, 16#34,
+ 16#30, 16#30, 16#20, 16#42, 16#61, 16#64, 16#20, 16#52,
+ 16#65, 16#71, 16#75, 16#65, 16#73, 16#74, 16#34, 16#30,
+ 16#31, 16#20, 16#55, 16#6e, 16#61, 16#75, 16#74, 16#68,
+ 16#6f, 16#72, 16#69, 16#7a, 16#65, 16#64, 16#34, 16#30,
+ 16#33, 16#20, 16#46, 16#6f, 16#72, 16#62, 16#69, 16#64,
+ 16#64, 16#65, 16#6e, 16#34, 16#30, 16#34, 16#20, 16#4e,
+ 16#6f, 16#74, 16#20, 16#46, 16#6f, 16#75, 16#6e, 16#64,
+ 16#35, 16#30, 16#30, 16#20, 16#49, 16#6e, 16#74, 16#65,
+ 16#72, 16#6e, 16#61, 16#6c, 16#20, 16#53, 16#65, 16#72,
+ 16#76, 16#65, 16#72, 16#20, 16#45, 16#72, 16#72, 16#6f,
+ 16#72, 16#35, 16#30, 16#31, 16#20, 16#4e, 16#6f, 16#74,
+ 16#20, 16#49, 16#6d, 16#70, 16#6c, 16#65, 16#6d, 16#65,
+ 16#6e, 16#74, 16#65, 16#64, 16#35, 16#30, 16#33, 16#20,
+ 16#53, 16#65, 16#72, 16#76, 16#69, 16#63, 16#65, 16#20,
+ 16#55, 16#6e, 16#61, 16#76, 16#61, 16#69, 16#6c, 16#61,
+ 16#62, 16#6c, 16#65, 16#4a, 16#61, 16#6e, 16#20, 16#46,
+ 16#65, 16#62, 16#20, 16#4d, 16#61, 16#72, 16#20, 16#41,
+ 16#70, 16#72, 16#20, 16#4d, 16#61, 16#79, 16#20, 16#4a,
+ 16#75, 16#6e, 16#20, 16#4a, 16#75, 16#6c, 16#20, 16#41,
+ 16#75, 16#67, 16#20, 16#53, 16#65, 16#70, 16#74, 16#20,
+ 16#4f, 16#63, 16#74, 16#20, 16#4e, 16#6f, 16#76, 16#20,
+ 16#44, 16#65, 16#63, 16#20, 16#30, 16#30, 16#3a, 16#30,
+ 16#30, 16#3a, 16#30, 16#30, 16#20, 16#4d, 16#6f, 16#6e,
+ 16#2c, 16#20, 16#54, 16#75, 16#65, 16#2c, 16#20, 16#57,
+ 16#65, 16#64, 16#2c, 16#20, 16#54, 16#68, 16#75, 16#2c,
+ 16#20, 16#46, 16#72, 16#69, 16#2c, 16#20, 16#53, 16#61,
+ 16#74, 16#2c, 16#20, 16#53, 16#75, 16#6e, 16#2c, 16#20,
+ 16#47, 16#4d, 16#54, 16#63, 16#68, 16#75, 16#6e, 16#6b,
+ 16#65, 16#64, 16#2c, 16#74, 16#65, 16#78, 16#74, 16#2f,
+ 16#68, 16#74, 16#6d, 16#6c, 16#2c, 16#69, 16#6d, 16#61,
+ 16#67, 16#65, 16#2f, 16#70, 16#6e, 16#67, 16#2c, 16#69,
+ 16#6d, 16#61, 16#67, 16#65, 16#2f, 16#6a, 16#70, 16#67,
+ 16#2c, 16#69, 16#6d, 16#61, 16#67, 16#65, 16#2f, 16#67,
+ 16#69, 16#66, 16#2c, 16#61, 16#70, 16#70, 16#6c, 16#69,
+ 16#63, 16#61, 16#74, 16#69, 16#6f, 16#6e, 16#2f, 16#78,
+ 16#6d, 16#6c, 16#2c, 16#61, 16#70, 16#70, 16#6c, 16#69,
+ 16#63, 16#61, 16#74, 16#69, 16#6f, 16#6e, 16#2f, 16#78,
+ 16#68, 16#74, 16#6d, 16#6c, 16#2b, 16#78, 16#6d, 16#6c,
+ 16#2c, 16#74, 16#65, 16#78, 16#74, 16#2f, 16#70, 16#6c,
+ 16#61, 16#69, 16#6e, 16#2c, 16#74, 16#65, 16#78, 16#74,
+ 16#2f, 16#6a, 16#61, 16#76, 16#61, 16#73, 16#63, 16#72,
+ 16#69, 16#70, 16#74, 16#2c, 16#70, 16#75, 16#62, 16#6c,
+ 16#69, 16#63, 16#70, 16#72, 16#69, 16#76, 16#61, 16#74,
+ 16#65, 16#6d, 16#61, 16#78, 16#2d, 16#61, 16#67, 16#65,
+ 16#3d, 16#67, 16#7a, 16#69, 16#70, 16#2c, 16#64, 16#65,
+ 16#66, 16#6c, 16#61, 16#74, 16#65, 16#2c, 16#73, 16#64,
+ 16#63, 16#68, 16#63, 16#68, 16#61, 16#72, 16#73, 16#65,
+ 16#74, 16#3d, 16#75, 16#74, 16#66, 16#2d, 16#38, 16#63,
+ 16#68, 16#61, 16#72, 16#73, 16#65, 16#74, 16#3d, 16#69,
+ 16#73, 16#6f, 16#2d, 16#38, 16#38, 16#35, 16#39, 16#2d,
+ 16#31, 16#2c, 16#75, 16#74, 16#66, 16#2d, 16#2c, 16#2a,
+ 16#2c, 16#65, 16#6e, 16#71, 16#3d, 16#30, 16#2e >>).
diff --git a/src/cowlib.app.src b/src/cowlib.app.src
new file mode 100644
index 0000000..c5d0d62
--- /dev/null
+++ b/src/cowlib.app.src
@@ -0,0 +1,24 @@
+%% Copyright (c) 2013, Loïc Hoguin <[email protected]>
+%%
+%% 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.
+
+{application, cowlib, [
+ {description, "Support library for manipulating Web protocols."},
+ {vsn, "0.1.0"},
+ {modules, []},
+ {registered, []},
+ {applications, [
+ kernel,
+ stdlib
+ ]}
+]}.