aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md20
-rw-r--r--Makefile2
-rw-r--r--README.md6
-rw-r--r--erlang.mk102
-rw-r--r--examples/elixir_hello_world/mix.exs2
-rw-r--r--manual/cowboy_websocket.md6
-rw-r--r--rebar.config2
-rw-r--r--src/cowboy_handler.erl86
-rw-r--r--src/cowboy_protocol.erl29
-rw-r--r--src/cowboy_req.erl14
-rw-r--r--src/cowboy_rest.erl184
-rw-r--r--src/cowboy_spdy.erl18
-rw-r--r--src/cowboy_websocket.erl88
13 files changed, 279 insertions, 280 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9e2fa32..abab6ae 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -127,6 +127,26 @@ Committing
You MUST ensure that all commits pass all tests and do not have extra
Dialyzer warnings.
+Running tests is fairly straightforward.
+
+``` bash
+make tests
+```
+
+Running Dialyzer requires some initial setup. You need to build the PLT
+file that Dialyzer will use for its analysis. This is a one-time operation.
+Dialyzer will take care of updating that file when needed.
+
+``` bash
+make build-plt
+```
+
+Once that is done, you can run Dialyzer.
+
+``` bash
+make dialyze
+```
+
You MUST put all the related work in a single commit. Fixing a bug is one
commit, adding a feature is one commit, adding two features is two commits.
diff --git a/Makefile b/Makefile
index 79c2b6c..202b343 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,7 @@ PLT_APPS = crypto public_key ssl
DEPS = ranch
TEST_DEPS = ct_helper
-dep_ranch = https://github.com/extend/ranch.git 0.8.4
+dep_ranch = https://github.com/extend/ranch.git 0.8.5
dep_ct_helper = https://github.com/extend/ct_helper.git master
# Standard targets.
diff --git a/README.md b/README.md
index a543ede..92f2563 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,12 @@ Because it uses Ranch for managing connections, Cowboy can easily be
No parameterized module. No process dictionary. **Clean** Erlang code.
+Sponsors
+--------
+
+The SPDY protocol development is sponsored
+by [LeoFS Cloud Storage](http://www.leofs.org).
+
Getting Started
---------------
diff --git a/erlang.mk b/erlang.mk
index d353c33..617abdc 100644
--- a/erlang.mk
+++ b/erlang.mk
@@ -12,6 +12,21 @@
# 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.tsv
+
+define get_pkg_file
+ wget -O $(PKG_FILE) $(PKG_FILE_URL)
+endef
+
# Verbosity and tweaks.
V ?= 0
@@ -19,9 +34,12 @@ V ?= 0
appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src;
appsrc_verbose = $(appsrc_verbose_$(V))
-erlc_verbose_0 = @echo " ERLC " $(filter-out %.dtl,$(?F));
+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))
@@ -36,11 +54,17 @@ gen_verbose = $(gen_verbose_$(V))
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.
+ERL_LIBS ?= $(DEPS_DIR)
+export ERL_LIBS
+
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 ?=
@@ -52,19 +76,25 @@ 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 \
+ $(eval MODULES := $(shell find ebin -type f -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 erlc -v $(ERLC_OPTS) -o ebin/ -pa ebin/ \
- $(COMPILE_FIRST_PATHS) $(1)
+ $(erlc_verbose) 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/erlydtl/ebin/ -eval ' \
+ $(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"), \
@@ -74,10 +104,16 @@ define compile_dtl
init:stop()'
endef
-ebin/$(PROJECT).app: src/*.erl $(wildcard src/*.core) $(wildcard templates/*.dtl)
+ebin/$(PROJECT).app: $(shell find src -type f -name \*.erl) \
+ $(shell find src -type f -name \*.core) \
+ $(shell find src -type f -name \*.xrl) \
+ $(shell find src -type f -name \*.yrl) \
+ $(shell find templates -type f -name \*.dtl 2>/dev/null)
@mkdir -p ebin/
- $(if $(strip $(filter-out %.dtl,$?)), \
- $(call compile_erl,$(filter-out %.dtl,$?)))
+ $(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,$?)))
@@ -88,7 +124,14 @@ clean:
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
@@ -100,7 +143,13 @@ endef
$(foreach dep,$(DEPS),$(eval $(call dep_target,$(dep))))
deps: $(ALL_DEPS_DIRS)
- @for dep in $(ALL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
+ @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
@@ -122,13 +171,13 @@ 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 erlc -v $(ERLC_OPTS) -o test/ \
+ $(gen_verbose) 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 \
+ -pa $(realpath ebin) $(DEPS_DIR)/*/ebin \
-dir test \
-logdir logs
# -cover test/cover.spec
@@ -138,8 +187,11 @@ 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)
+ @if [ -d "test" ] ; \
+ then \
+ mkdir -p logs/ ; \
+ $(CT_RUN) -suite $(CT_SUITES_FULL) ; \
+ fi
$(gen_verbose) rm -f test/*.beam
# Dialyzer.
@@ -154,3 +206,27 @@ build-plt: deps app
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/examples/elixir_hello_world/mix.exs b/examples/elixir_hello_world/mix.exs
index 9055175..00297bf 100644
--- a/examples/elixir_hello_world/mix.exs
+++ b/examples/elixir_hello_world/mix.exs
@@ -14,7 +14,7 @@ defmodule ElixirHelloWorld.Mixfile do
end
defp deps do
- [ {:ranch, github: "extend/ranch", tag: "0.8.4"},
+ [ {:ranch, github: "extend/ranch", tag: "0.8.5"},
{:cowboy, github: "extend/cowboy"} ]
end
end
diff --git a/manual/cowboy_websocket.md b/manual/cowboy_websocket.md
index ae3ca1b..9a81878 100644
--- a/manual/cowboy_websocket.md
+++ b/manual/cowboy_websocket.md
@@ -22,6 +22,12 @@ Types
Meta values
-----------
+### websocket_compress
+
+> Type: true | false
+>
+> Whether a websocket compression extension in in use.
+
### websocket_version
> Type: 7 | 8 | 13
diff --git a/rebar.config b/rebar.config
index 443949a..edd3948 100644
--- a/rebar.config
+++ b/rebar.config
@@ -1,3 +1,3 @@
{deps, [
- {ranch, ".*", {git, "git://github.com/extend/ranch.git", "0.8.4"}}
+ {ranch, ".*", {git, "git://github.com/extend/ranch.git", "0.8.5"}}
]}.
diff --git a/src/cowboy_handler.erl b/src/cowboy_handler.erl
index 2074b4e..fcbfe55 100644
--- a/src/cowboy_handler.erl
+++ b/src/cowboy_handler.erl
@@ -90,15 +90,14 @@ handler_init(Req, State, Handler, HandlerOpts) ->
{upgrade, protocol, Module, Req2, HandlerOpts2} ->
upgrade_protocol(Req2, State, Handler, HandlerOpts2, Module)
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n"
- "** Options were ~p~n"
- "** Request was ~p~n"
- "** Stacktrace: ~p~n~n",
- [Handler, init, 3, Class, Reason, HandlerOpts,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req, State)
+ cowboy_req:maybe_reply(500, Req),
+ erlang:Class([
+ {reason, Reason},
+ {mfa, {Handler, init, 3}},
+ {stacktrace, erlang:get_stacktrace()},
+ {req, cowboy_req:to_list(Req)},
+ {opts, HandlerOpts}
+ ])
end.
-spec upgrade_protocol(Req, #state{}, module(), any(), module())
@@ -121,16 +120,15 @@ handler_handle(Req, State, Handler, HandlerState) ->
terminate_request(Req2, State, Handler, HandlerState2,
{normal, shutdown})
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n"
- "** Handler state was ~p~n"
- "** Request was ~p~n"
- "** Stacktrace: ~p~n~n",
- [Handler, handle, 2, Class, Reason, HandlerState,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
+ cowboy_req:maybe_reply(500, Req),
handler_terminate(Req, Handler, HandlerState, Reason),
- error_terminate(Req, State)
+ erlang:Class([
+ {reason, Reason},
+ {mfa, {Handler, handle, 2}},
+ {stacktrace, erlang:get_stacktrace()},
+ {req, cowboy_req:to_list(Req)},
+ {state, HandlerState}
+ ])
end.
%% Update the state if the response was sent in the callback.
@@ -195,7 +193,8 @@ handler_loop(Req, State=#state{loop_buffer_size=NbBytes,
if NbBytes2 > Threshold ->
_ = handler_terminate(Req, Handler, HandlerState,
{error, overflow}),
- error_terminate(Req, State);
+ cowboy_req:maybe_reply(500, Req),
+ exit(normal);
true ->
Req2 = cowboy_req:append_buffer(Data, Req),
State2 = handler_loop_timeout(State#state{
@@ -232,7 +231,8 @@ handler_loop(Req, State=#state{loop_buffer_size=NbBytes,
-> {ok, Req, cowboy_middleware:env()}
| {error, 500, Req} | {suspend, module(), atom(), [any()]}
when Req::cowboy_req:req().
-handler_call(Req, State, Handler, HandlerState, Message) ->
+handler_call(Req, State=#state{resp_sent=RespSent},
+ Handler, HandlerState, Message) ->
try Handler:info(Message, Req, HandlerState) of
{ok, Req2, HandlerState2} ->
handler_after_loop(Req2, State, Handler, HandlerState2,
@@ -243,16 +243,19 @@ handler_call(Req, State, Handler, HandlerState, Message) ->
handler_after_callback(Req2, State#state{hibernate=true},
Handler, HandlerState2)
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n"
- "** Handler state was ~p~n"
- "** Request was ~p~n"
- "** Stacktrace: ~p~n~n",
- [Handler, info, 3, Class, Reason, HandlerState,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
+ if RespSent ->
+ ok;
+ true ->
+ cowboy_req:maybe_reply(500, Req)
+ end,
handler_terminate(Req, Handler, HandlerState, Reason),
- error_terminate(Req, State)
+ erlang:Class([
+ {reason, Reason},
+ {mfa, {Handler, info, 3}},
+ {stacktrace, erlang:get_stacktrace()},
+ {req, cowboy_req:to_list(Req)},
+ {state, HandlerState}
+ ])
end.
%% It is sometimes important to make a socket passive as it was initially
@@ -287,21 +290,12 @@ handler_terminate(Req, Handler, HandlerState, Reason) ->
try
Handler:terminate(Reason, cowboy_req:lock(Req), HandlerState)
catch Class:Reason2 ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n"
- "** Handler state was ~p~n"
- "** Request was ~p~n"
- "** Stacktrace: ~p~n~n",
- [Handler, terminate, 3, Class, Reason2, HandlerState,
- cowboy_req:to_list(Req), erlang:get_stacktrace()])
+ erlang:Class([
+ {reason, Reason2},
+ {mfa, {Handler, terminate, 3}},
+ {stacktrace, erlang:get_stacktrace()},
+ {req, cowboy_req:to_list(Req)},
+ {state, HandlerState},
+ {terminate_reason, Reason}
+ ])
end.
-
-%% Only send an error reply if there is no resp_sent message.
--spec error_terminate(Req, #state{})
- -> {error, 500, Req} | {halt, Req} when Req::cowboy_req:req().
-error_terminate(Req, #state{resp_sent=true}) ->
- %% Close the connection, but do not attempt sending a reply.
- {halt, cowboy_req:set([{connection, close}, {resp_state, done}], Req)};
-error_terminate(Req, _) ->
- {error, 500, Req}.
diff --git a/src/cowboy_protocol.erl b/src/cowboy_protocol.erl
index b42f524..40be2c0 100644
--- a/src/cowboy_protocol.erl
+++ b/src/cowboy_protocol.erl
@@ -573,29 +573,16 @@ next_request(Req, State=#state{req_keepalive=Keepalive, timeout=Timeout},
end
end.
-%% Only send an error reply if there is no resp_sent message.
--spec error_terminate(cowboy:http_status(), cowboy_req:req(), #state{}) -> ok.
-error_terminate(Code, Req, State) ->
- receive
- {cowboy_req, resp_sent} -> ok
- after 0 ->
- _ = cowboy_req:reply(Code, Req),
- ok
- end,
- terminate(State).
-
-%% Only send an error reply if there is no resp_sent message.
-spec error_terminate(cowboy:http_status(), #state{}) -> ok.
-error_terminate(Code, State=#state{socket=Socket, transport=Transport,
+error_terminate(Status, State=#state{socket=Socket, transport=Transport,
compress=Compress, onresponse=OnResponse}) ->
- receive
- {cowboy_req, resp_sent} -> ok
- after 0 ->
- _ = cowboy_req:reply(Code, cowboy_req:new(Socket, Transport,
- undefined, <<"GET">>, <<>>, <<>>, 'HTTP/1.1', [], <<>>,
- undefined, <<>>, false, Compress, OnResponse)),
- ok
- end,
+ error_terminate(Status, cowboy_req:new(Socket, Transport,
+ undefined, <<"GET">>, <<>>, <<>>, 'HTTP/1.1', [], <<>>,
+ undefined, <<>>, false, Compress, OnResponse), State).
+
+-spec error_terminate(cowboy:http_status(), cowboy_req:req(), #state{}) -> ok.
+error_terminate(Status, Req, State) ->
+ cowboy_req:maybe_reply(Status, Req),
terminate(State).
-spec terminate(#state{}) -> ok.
diff --git a/src/cowboy_req.erl b/src/cowboy_req.erl
index 66abcf8..32ff7b0 100644
--- a/src/cowboy_req.erl
+++ b/src/cowboy_req.erl
@@ -101,6 +101,7 @@
-export([chunked_reply/3]).
-export([chunk/2]).
-export([upgrade_reply/3]).
+-export([maybe_reply/2]).
-export([ensure_response/2]).
%% Private setter/getter API.
@@ -1120,6 +1121,19 @@ upgrade_reply(Status, Headers, Req=#http_req{transport=Transport,
], <<>>, Req),
{ok, Req2#http_req{resp_state=done, resp_headers=[], resp_body= <<>>}}.
+%% @doc Send a reply if one hasn't been sent already.
+%%
+%% Meant to be used internally for sending errors after crashes.
+%% @private
+-spec maybe_reply(cowboy:http_status(), req()) -> ok.
+maybe_reply(Status, Req) ->
+ receive
+ {cowboy_req, resp_sent} -> ok
+ after 0 ->
+ _ = cowboy_req:reply(Status, Req),
+ ok
+ end.
+
%% @doc Ensure the response has been sent fully.
%% @private
-spec ensure_response(req(), cowboy:http_status()) -> ok.
diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl
index 34bfce1..862ebbf 100644
--- a/src/cowboy_rest.erl
+++ b/src/cowboy_rest.erl
@@ -66,30 +66,26 @@
-> {ok, Req, Env} | {error, 500, Req}
when Req::cowboy_req:req(), Env::cowboy_middleware:env().
upgrade(Req, Env, Handler, HandlerOpts) ->
- try
- Method = cowboy_req:get(method, Req),
- case erlang:function_exported(Handler, rest_init, 2) of
- true ->
- try Handler:rest_init(Req, HandlerOpts) of
- {ok, Req2, HandlerState} ->
- service_available(Req2, #state{env=Env, method=Method,
- handler=Handler, handler_state=HandlerState})
- catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Options were ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [Handler, rest_init, 2, Class, Reason, HandlerOpts,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- {error, 500, Req}
- end;
- false ->
- service_available(Req, #state{env=Env, method=Method,
- handler=Handler})
- end
- catch
- throw:{?MODULE, error} ->
- {error, 500, Req}
+ Method = cowboy_req:get(method, Req),
+ case erlang:function_exported(Handler, rest_init, 2) of
+ true ->
+ try Handler:rest_init(Req, HandlerOpts) of
+ {ok, Req2, HandlerState} ->
+ service_available(Req2, #state{env=Env, method=Method,
+ handler=Handler, handler_state=HandlerState})
+ catch Class:Reason ->
+ cowboy_req:maybe_reply(500, Req),
+ erlang:Class([
+ {reason, Reason},
+ {mfa, {Handler, rest_init, 2}},
+ {stacktrace, erlang:get_stacktrace()},
+ {req, cowboy_req:to_list(Req)},
+ {opts, HandlerOpts}
+ ])
+ end;
+ false ->
+ service_available(Req, #state{env=Env, method=Method,
+ handler=Handler})
end.
service_available(Req, State) ->
@@ -516,14 +512,7 @@ variances(Req, State=#state{content_types_p=CTP,
resource_exists(Req3, State2)
end
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [State#state.handler, variances, 2,
- Class, Reason, State#state.handler_state,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req, State)
+ error_terminate(Req, State, Class, Reason, variances)
end.
variances(Req, State, Variances) ->
@@ -559,14 +548,7 @@ if_match(Req, State, EtagsList) ->
false -> precondition_failed(Req2, State2)
end
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [State#state.handler, generate_etag, 2,
- Class, Reason, State#state.handler_state,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req, State)
+ error_terminate(Req, State, Class, Reason, generate_etag)
end.
if_match_must_not_exist(Req, State) ->
@@ -594,14 +576,7 @@ if_unmodified_since(Req, State, IfUnmodifiedSince) ->
false -> if_none_match_exists(Req2, State2)
end
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [State#state.handler, last_modified, 2,
- Class, Reason, State#state.handler_state,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req, State)
+ error_terminate(Req, State, Class, Reason, last_modified)
end.
if_none_match_exists(Req, State) ->
@@ -627,14 +602,7 @@ if_none_match(Req, State, EtagsList) ->
end
end
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [State#state.handler, generate_etag, 2,
- Class, Reason, State#state.handler_state,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req, State)
+ error_terminate(Req, State, Class, Reason, generate_etag)
end.
precondition_is_head_get(Req, State=#state{method=Method})
@@ -669,14 +637,7 @@ if_modified_since(Req, State, IfModifiedSince) ->
false -> not_modified(Req2, State2)
end
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [State#state.handler, last_modified, 2,
- Class, Reason, State#state.handler_state,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req, State)
+ error_terminate(Req, State, Class, Reason, last_modified)
end.
not_modified(Req, State) ->
@@ -687,24 +648,10 @@ not_modified(Req, State) ->
{Req4, State3} ->
respond(Req4, State3, 304)
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [State#state.handler, expires, 2,
- Class, Reason, State#state.handler_state,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req2, State)
+ error_terminate(Req, State, Class, Reason, expires)
end
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [State#state.handler, generate_etag, 2,
- Class, Reason, State#state.handler_state,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req2, State)
+ error_terminate(Req, State, Class, Reason, generate_etag)
end.
precondition_failed(Req, State) ->
@@ -829,17 +776,8 @@ choose_content_type(Req, State, {Type, SubType, Param},
choose_content_type(Req, State, ContentType, [_Any|Tail]) ->
choose_content_type(Req, State, ContentType, Tail).
-process_content_type(Req, State=#state{method=Method,
- handler=Handler, handler_state=HandlerState,
- exists=Exists}, Fun) ->
- case call(Req, State, Fun) of
- no_call ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating; "
- "function ~p/~p was not exported~n"
- "** Request was ~p~n** State was ~p~n~n",
- [Handler, Fun, 2, cowboy_req:to_list(Req), HandlerState]),
- {error, 500, Req};
+process_content_type(Req, State=#state{method=Method, exists=Exists}, Fun) ->
+ try case call(Req, State, Fun) of
{halt, Req2, HandlerState2} ->
terminate(Req2, State#state{handler_state=HandlerState2});
{true, Req2, HandlerState2} when Exists ->
@@ -859,6 +797,8 @@ process_content_type(Req, State=#state{method=Method,
Exists -> respond(Req3, State2, 303);
true -> respond(Req3, State2, 201)
end
+ end catch Class:Reason = {case_clause, no_call} ->
+ error_terminate(Req, State, Class, Reason, Fun)
end.
%% If the resource is new and has been created at another location
@@ -881,14 +821,7 @@ set_resp_body_etag(Req, State) ->
{Req2, State2} ->
set_resp_body_last_modified(Req2, State2)
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [State#state.handler, generate_etag, 2,
- Class, Reason, State#state.handler_state,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req, State)
+ error_terminate(Req, State, Class, Reason, generate_etag)
end.
%% Set the Last-Modified header if any for the response provided.
@@ -905,14 +838,7 @@ set_resp_body_last_modified(Req, State) ->
set_resp_body_expires(Req3, State2)
end
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [State#state.handler, last_modified, 2,
- Class, Reason, State#state.handler_state,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req, State)
+ error_terminate(Req, State, Class, Reason, last_modified)
end.
%% Set the Expires header if any for the response provided.
@@ -921,29 +847,14 @@ set_resp_body_expires(Req, State) ->
{Req2, State2} ->
set_resp_body(Req2, State2)
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [State#state.handler, expires, 2,
- Class, Reason, State#state.handler_state,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req, State)
+ error_terminate(Req, State, Class, Reason, expires)
end.
%% Set the response headers and call the callback found using
%% content_types_provided/2 to obtain the request body and add
%% it to the response.
-set_resp_body(Req, State=#state{handler=Handler, handler_state=HandlerState,
- content_type_a={_Type, Callback}}) ->
- case call(Req, State, Callback) of
- no_call ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating; "
- "function ~p/~p was not exported~n"
- "** Request was ~p~n** State was ~p~n~n",
- [Handler, Callback, 2, cowboy_req:to_list(Req), HandlerState]),
- {error, 500, Req};
+set_resp_body(Req, State=#state{content_type_a={_, Callback}}) ->
+ try case call(Req, State, Callback) of
{halt, Req2, HandlerState2} ->
terminate(Req2, State#state{handler_state=HandlerState2});
{Body, Req2, HandlerState2} ->
@@ -959,6 +870,8 @@ set_resp_body(Req, State=#state{handler=Handler, handler_state=HandlerState,
cowboy_req:set_resp_body(Body, Req2)
end,
multiple_choices(Req3, State2)
+ end catch Class:Reason = {case_clause, no_call} ->
+ error_terminate(Req, State, Class, Reason, Callback)
end.
multiple_choices(Req, State) ->
@@ -1057,13 +970,7 @@ call(Req, State=#state{handler=Handler, handler_state=HandlerState},
try
Handler:Callback(Req, HandlerState)
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [Handler, Callback, 2, Class, Reason, HandlerState,
- cowboy_req:to_list(Req), erlang:get_stacktrace()]),
- error_terminate(Req, State)
+ error_terminate(Req, State, Class, Reason, Callback)
end;
false ->
no_call
@@ -1089,10 +996,17 @@ terminate(Req, State=#state{env=Env}) ->
rest_terminate(Req, State),
{ok, Req, [{result, ok}|Env]}.
--spec error_terminate(cowboy_req:req(), #state{}) -> no_return().
-error_terminate(Req, State) ->
+error_terminate(Req, State=#state{handler=Handler, handler_state=HandlerState},
+ Class, Reason, Callback) ->
rest_terminate(Req, State),
- erlang:raise(throw, {?MODULE, error}, erlang:get_stacktrace()).
+ cowboy_req:maybe_reply(500, Req),
+ erlang:Class([
+ {reason, Reason},
+ {mfa, {Handler, Callback, 2}},
+ {stacktrace, erlang:get_stacktrace()},
+ {req, cowboy_req:to_list(Req)},
+ {state, HandlerState}
+ ]).
rest_terminate(Req, #state{handler=Handler, handler_state=HandlerState}) ->
case erlang:function_exported(Handler, rest_terminate, 2) of
diff --git a/src/cowboy_spdy.erl b/src/cowboy_spdy.erl
index 182e6da..cc4d867 100644
--- a/src/cowboy_spdy.erl
+++ b/src/cowboy_spdy.erl
@@ -520,8 +520,8 @@ execute(Req, Env, [Middleware|Tail]) ->
[Env, Tail, Module, Function, Args]);
{halt, Req2} ->
cowboy_req:ensure_response(Req2, 204);
- {error, Code, Req2} ->
- error_terminate(Code, Req2)
+ {error, Status, Req2} ->
+ cowboy_req:maybe_reply(Status, Req2)
end.
%% @private
@@ -536,18 +536,8 @@ resume(Env, Tail, Module, Function, Args) ->
[Env, Tail, Module2, Function2, Args2]);
{halt, Req2} ->
cowboy_req:ensure_response(Req2, 204);
- {error, Code, Req2} ->
- error_terminate(Code, Req2)
- end.
-
-%% Only send an error reply if there is no resp_sent message.
--spec error_terminate(cowboy:http_status(), cowboy_req:req()) -> ok.
-error_terminate(Code, Req) ->
- receive
- {cowboy_req, resp_sent} -> ok
- after 0 ->
- _ = cowboy_req:reply(Code, Req),
- ok
+ {error, Status, Req2} ->
+ cowboy_req:maybe_reply(Status, Req2)
end.
%% Reply functions used by cowboy_req.
diff --git a/src/cowboy_websocket.erl b/src/cowboy_websocket.erl
index 75c55da..40046e1 100644
--- a/src/cowboy_websocket.erl
+++ b/src/cowboy_websocket.erl
@@ -48,7 +48,6 @@
socket = undefined :: inet:socket(),
transport = undefined :: module(),
handler :: module(),
- handler_opts :: any(),
key = undefined :: undefined | binary(),
timeout = infinity :: timeout(),
timeout_ref = undefined :: undefined | reference(),
@@ -75,10 +74,13 @@ upgrade(Req, Env, Handler, HandlerOpts) ->
ranch:remove_connection(Ref),
[Socket, Transport] = cowboy_req:get([socket, transport], Req),
State = #state{env=Env, socket=Socket, transport=Transport,
- handler=Handler, handler_opts=HandlerOpts},
- case catch websocket_upgrade(State, Req) of
- {ok, State2, Req2} -> handler_init(State2, Req2);
- {'EXIT', _Reason} -> upgrade_error(Req, Env)
+ handler=Handler},
+ try websocket_upgrade(State, Req) of
+ {ok, State2, Req2} ->
+ handler_init(State2, Req2, HandlerOpts)
+ catch _:_ ->
+ cowboy_req:maybe_reply(400, Req),
+ exit(normal)
end.
-spec websocket_upgrade(#state{}, Req)
@@ -121,20 +123,20 @@ websocket_extensions(State, Req) ->
deflate_frame = true,
inflate_state = Inflate,
deflate_state = Deflate
- }, Req2};
+ }, cowboy_req:set_meta(websocket_compress, true, Req2)};
_ ->
- {ok, State, Req2}
+ {ok, State, cowboy_req:set_meta(websocket_compress, false, Req2)}
end;
_ ->
- {ok, State, Req}
+ {ok, State, cowboy_req:set_meta(websocket_compress, false, Req)}
end.
--spec handler_init(#state{}, Req)
+-spec handler_init(#state{}, Req, any())
-> {ok, Req, cowboy_middleware:env()} | {error, 400, Req}
| {suspend, module(), atom(), [any()]}
when Req::cowboy_req:req().
handler_init(State=#state{env=Env, transport=Transport,
- handler=Handler, handler_opts=HandlerOpts}, Req) ->
+ handler=Handler}, Req, HandlerOpts) ->
try Handler:websocket_init(Transport:name(), Req, HandlerOpts) of
{ok, Req2, HandlerState} ->
websocket_handshake(State, Req2, HandlerState);
@@ -151,24 +153,14 @@ handler_init(State=#state{env=Env, transport=Transport,
cowboy_req:ensure_response(Req2, 400),
{ok, Req2, [{result, closed}|Env]}
catch Class:Reason ->
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Options were ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [Handler, websocket_init, 3, Class, Reason, HandlerOpts,
- cowboy_req:to_list(Req),erlang:get_stacktrace()]),
- upgrade_error(Req, Env)
- end.
-
-%% Only send an error reply if there is no resp_sent message.
--spec upgrade_error(Req, Env) -> {ok, Req, Env} | {error, 400, Req}
- when Req::cowboy_req:req(), Env::cowboy_middleware:env().
-upgrade_error(Req, Env) ->
- receive
- {cowboy_req, resp_sent} ->
- {ok, Req, [{result, closed}|Env]}
- after 0 ->
- {error, 400, Req}
+ cowboy_req:maybe_reply(400, Req),
+ erlang:Class([
+ {reason, Reason},
+ {mfa, {Handler, websocket_init, 3}},
+ {stacktrace, erlang:get_stacktrace()},
+ {req, cowboy_req:to_list(Req)},
+ {opts, HandlerOpts}
+ ])
end.
-spec websocket_handshake(#state{}, Req, any())
@@ -601,8 +593,8 @@ websocket_dispatch(State, Req, HandlerState, RemainingData, 10, Payload) ->
-> {ok, Req, cowboy_middleware:env()}
| {suspend, module(), atom(), [any()]}
when Req::cowboy_req:req().
-handler_call(State=#state{handler=Handler, handler_opts=HandlerOpts}, Req,
- HandlerState, RemainingData, Callback, Message, NextState) ->
+handler_call(State=#state{handler=Handler}, Req, HandlerState,
+ RemainingData, Callback, Message, NextState) ->
try Handler:Callback(Message, Req, HandlerState) of
{ok, Req2, HandlerState2} ->
NextState(State, Req2, HandlerState2, RemainingData);
@@ -656,15 +648,15 @@ handler_call(State=#state{handler=Handler, handler_opts=HandlerOpts}, Req,
{shutdown, Req2, HandlerState2} ->
websocket_close(State, Req2, HandlerState2, {normal, shutdown})
catch Class:Reason ->
- PLReq = cowboy_req:to_list(Req),
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Message was ~p~n"
- "** Options were ~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [Handler, Callback, 3, Class, Reason, Message, HandlerOpts,
- HandlerState, PLReq, erlang:get_stacktrace()]),
- websocket_close(State, Req, HandlerState, {error, handler})
+ _ = websocket_close(State, Req, HandlerState, {error, handler}),
+ erlang:Class([
+ {reason, Reason},
+ {mfa, {Handler, Callback, 3}},
+ {stacktrace, erlang:get_stacktrace()},
+ {msg, Message},
+ {req, cowboy_req:to_list(Req)},
+ {state, HandlerState}
+ ])
end.
websocket_opcode(text) -> 1;
@@ -763,19 +755,19 @@ websocket_close(State=#state{socket=Socket, transport=Transport},
-spec handler_terminate(#state{}, Req, any(), atom() | {atom(), atom()})
-> {ok, Req, cowboy_middleware:env()}
when Req::cowboy_req:req().
-handler_terminate(#state{env=Env, handler=Handler, handler_opts=HandlerOpts},
+handler_terminate(#state{env=Env, handler=Handler},
Req, HandlerState, TerminateReason) ->
try
Handler:websocket_terminate(TerminateReason, Req, HandlerState)
catch Class:Reason ->
- PLReq = cowboy_req:to_list(Req),
- error_logger:error_msg(
- "** Cowboy handler ~p terminating in ~p/~p~n"
- " for the reason ~p:~p~n** Initial reason was ~p~n"
- "** Options were ~p~n** Handler state was ~p~n"
- "** Request was ~p~n** Stacktrace: ~p~n~n",
- [Handler, websocket_terminate, 3, Class, Reason, TerminateReason,
- HandlerOpts, HandlerState, PLReq, erlang:get_stacktrace()])
+ erlang:Class([
+ {reason, Reason},
+ {mfa, {Handler, websocket_terminate, 3}},
+ {stacktrace, erlang:get_stacktrace()},
+ {req, cowboy_req:to_list(Req)},
+ {state, HandlerState},
+ {terminate_reason, TerminateReason}
+ ])
end,
{ok, Req, [{result, closed}|Env]}.