aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cowboy_bstr.erl49
-rw-r--r--src/cowboy_protocol.erl4
-rw-r--r--src/cowboy_req.erl22
-rw-r--r--src/cowboy_rest.erl6
-rw-r--r--src/cowboy_websocket.erl8
5 files changed, 67 insertions, 22 deletions
diff --git a/src/cowboy_bstr.erl b/src/cowboy_bstr.erl
index e906de7..bc6818f 100644
--- a/src/cowboy_bstr.erl
+++ b/src/cowboy_bstr.erl
@@ -16,16 +16,40 @@
-module(cowboy_bstr).
%% Binary strings.
+-export([capitalize_token/1]).
-export([to_lower/1]).
%% Characters.
-export([char_to_lower/1]).
-export([char_to_upper/1]).
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+-endif.
+
+%% @doc Capitalize a token.
+%%
+%% The first letter and all letters after a dash are capitalized.
+%% This is the form seen for header names in the HTTP/1.1 RFC and
+%% others. Note that using this form isn't required, as header name
+%% are case insensitive, and it is only provided for use with eventual
+%% badly implemented clients.
+-spec capitalize_token(B) -> B when B::binary().
+capitalize_token(B) ->
+ capitalize_token(B, true, <<>>).
+capitalize_token(<<>>, _, Acc) ->
+ Acc;
+capitalize_token(<< $-, Rest/bits >>, _, Acc) ->
+ capitalize_token(Rest, true, << Acc/binary, $- >>);
+capitalize_token(<< C, Rest/bits >>, true, Acc) ->
+ capitalize_token(Rest, false, << Acc/binary, (char_to_upper(C)) >>);
+capitalize_token(<< C, Rest/bits >>, false, Acc) ->
+ capitalize_token(Rest, false, << Acc/binary, (char_to_lower(C)) >>).
+
%% @doc Convert a binary string to lowercase.
--spec to_lower(binary()) -> binary().
-to_lower(L) ->
- << << (char_to_lower(C)) >> || << C >> <= L >>.
+-spec to_lower(B) -> B when B::binary().
+to_lower(B) ->
+ << << (char_to_lower(C)) >> || << C >> <= B >>.
%% @doc Convert [A-Z] characters to lowercase.
%% @end
@@ -88,3 +112,22 @@ char_to_upper($x) -> $X;
char_to_upper($y) -> $Y;
char_to_upper($z) -> $Z;
char_to_upper(Ch) -> Ch.
+
+%% Tests.
+
+-ifdef(TEST).
+
+capitalize_token_test_() ->
+ %% {Header, Result}
+ Tests = [
+ {<<"heLLo-woRld">>, <<"Hello-World">>},
+ {<<"Sec-Websocket-Version">>, <<"Sec-Websocket-Version">>},
+ {<<"Sec-WebSocket-Version">>, <<"Sec-Websocket-Version">>},
+ {<<"sec-websocket-version">>, <<"Sec-Websocket-Version">>},
+ {<<"SEC-WEBSOCKET-VERSION">>, <<"Sec-Websocket-Version">>},
+ {<<"Sec-WebSocket--Version">>, <<"Sec-Websocket--Version">>},
+ {<<"Sec-WebSocket---Version">>, <<"Sec-Websocket---Version">>}
+ ],
+ [{H, fun() -> R = capitalize_token(H) end} || {H, R} <- Tests].
+
+-endif.
diff --git a/src/cowboy_protocol.erl b/src/cowboy_protocol.erl
index 0e9982b..a0f571b 100644
--- a/src/cowboy_protocol.erl
+++ b/src/cowboy_protocol.erl
@@ -30,7 +30,7 @@
%% <dt>max_headers</dt><dd>Max number of headers allowed.
%% Defaults to 100.</dd>
%% <dt>max_keepalive</dt><dd>Max number of requests allowed in a single
-%% keep-alive session. Defaults to infinity.</dd>
+%% keep-alive session. Defaults to 100.</dd>
%% <dt>max_request_line_length</dt><dd>Max length allowed for the request
%% line. Defaults to 4096.</dd>
%% <dt>middlewares</dt><dd>The list of middlewares to execute when a
@@ -107,7 +107,7 @@ init(ListenerPid, Socket, Transport, Opts) ->
MaxHeaderNameLength = get_value(max_header_name_length, Opts, 64),
MaxHeaderValueLength = get_value(max_header_value_length, Opts, 4096),
MaxHeaders = get_value(max_headers, Opts, 100),
- MaxKeepalive = get_value(max_keepalive, Opts, infinity),
+ MaxKeepalive = get_value(max_keepalive, Opts, 100),
MaxRequestLineLength = get_value(max_request_line_length, Opts, 4096),
Middlewares = get_value(middlewares, Opts, [cowboy_router, cowboy_handler]),
Env = [{listener, ListenerPid}|get_value(env, Opts, [])],
diff --git a/src/cowboy_req.erl b/src/cowboy_req.erl
index 89758dd..7f7ef32 100644
--- a/src/cowboy_req.erl
+++ b/src/cowboy_req.erl
@@ -163,7 +163,8 @@
| {non_neg_integer(), resp_body_fun()},
%% Functions.
- onresponse = undefined :: undefined | cowboy_protocol:onresponse_fun()
+ onresponse = undefined :: undefined | already_called
+ | cowboy_protocol:onresponse_fun()
}).
-opaque req() :: #http_req{}.
@@ -555,11 +556,10 @@ set_meta(Name, Value, Req=#http_req{meta=Meta}) ->
%% Request Body API.
%% @doc Return whether the request message has a body.
--spec has_body(Req) -> {boolean(), Req} when Req::req().
+-spec has_body(cowboy_req:req()) -> boolean().
has_body(Req) ->
- Has = lists:keymember(<<"content-length">>, 1, Req#http_req.headers) orelse
- lists:keymember(<<"transfer-encoding">>, 1, Req#http_req.headers),
- {Has, Req}.
+ lists:keymember(<<"content-length">>, 1, Req#http_req.headers) orelse
+ lists:keymember(<<"transfer-encoding">>, 1, Req#http_req.headers).
%% @doc Return the request message body length, if known.
%%
@@ -728,7 +728,6 @@ skip_body(Req) ->
%% @doc Return the full body sent with the request, parsed as an
%% application/x-www-form-urlencoded string. Essentially a POST query string.
-%% @todo We need an option to limit the size of the body for QS too.
-spec body_qs(Req)
-> {ok, [{binary(), binary() | true}], Req} | {error, atom()}
when Req::req().
@@ -765,7 +764,6 @@ multipart_data(Req=#http_req{multipart={Length, Cont}}) ->
multipart_data(Req=#http_req{body_state=done}) ->
{eof, Req}.
-%% @todo Typespecs.
multipart_data(Req, Length, {headers, Headers, Cont}) ->
{headers, Headers, Req#http_req{multipart={Length, Cont}}};
multipart_data(Req, Length, {body, Data, Cont}) ->
@@ -868,6 +866,8 @@ has_resp_header(Name, #http_req{resp_headers=RespHeaders}) ->
%% @doc Return whether a body has been set for the response.
-spec has_resp_body(req()) -> boolean().
+has_resp_body(#http_req{resp_body=RespBody}) when is_function(RespBody) ->
+ true;
has_resp_body(#http_req{resp_body={Length, _}}) ->
Length > 0;
has_resp_body(#http_req{resp_body=RespBody}) ->
@@ -1163,13 +1163,17 @@ to_list(Req) ->
response(Status, Headers, RespHeaders, DefaultHeaders, Body, Req=#http_req{
socket=Socket, transport=Transport, version=Version,
pid=ReqPid, onresponse=OnResponse}) ->
- FullHeaders = response_merge_headers(Headers, RespHeaders, DefaultHeaders),
+ FullHeaders = case OnResponse of
+ already_called -> Headers;
+ _ -> response_merge_headers(Headers, RespHeaders, DefaultHeaders)
+ end,
Req2 = case OnResponse of
+ already_called -> Req;
undefined -> Req;
OnResponse -> OnResponse(Status, FullHeaders, Body,
%% Don't call 'onresponse' from the hook itself.
Req#http_req{resp_headers=[], resp_body= <<>>,
- onresponse=undefined})
+ onresponse=already_called})
end,
ReplyType = case Req2#http_req.resp_state of
waiting ->
diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl
index 963b2f7..f5bc22d 100644
--- a/src/cowboy_rest.erl
+++ b/src/cowboy_rest.erl
@@ -846,12 +846,6 @@ generate_etag(Req, State=#state{etag=undefined}) ->
case call(Req, State, generate_etag) of
no_call ->
{undefined, Req, State#state{etag=no_call}};
- %% Previously the return value from the generate_etag/2 callback was set
- %% as the value of the ETag header in the response. Therefore the only
- %% valid return type was `binary()'. If a handler returns a `binary()'
- %% it must be mapped to the expected type or it'll always fail to
- %% compare equal to any entity tags present in the request headers.
- %% @todo Remove support for binary return values after 0.6.
{Etag, Req2, HandlerState} when is_binary(Etag) ->
[Etag2] = cowboy_http:entity_tag_match(Etag),
{Etag2, Req2, State#state{handler_state=HandlerState, etag=Etag2}};
diff --git a/src/cowboy_websocket.erl b/src/cowboy_websocket.erl
index 4553aef..1b2ad3b 100644
--- a/src/cowboy_websocket.erl
+++ b/src/cowboy_websocket.erl
@@ -24,9 +24,12 @@
%% Internal.
-export([handler_loop/4]).
+-type close_code() :: 1000..4999.
+-export_type([close_code/0]).
+
-type frame() :: close | ping | pong
| {text | binary | close | ping | pong, binary()}
- | {close, 1000..4999, binary()}.
+ | {close, close_code(), binary()}.
-export_type([frame/0]).
-type opcode() :: 0 | 1 | 2 | 8 | 9 | 10.
@@ -645,7 +648,8 @@ websocket_send_many([Frame|Tail], State) ->
Error -> Error
end.
--spec websocket_close(#state{}, Req, any(), {atom(), atom()})
+-spec websocket_close(#state{}, Req, any(),
+ {atom(), atom()} | {remote, close_code(), binary()})
-> {ok, Req, cowboy_middleware:env()}
when Req::cowboy_req:req().
websocket_close(State=#state{socket=Socket, transport=Transport},