diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cowboy_bstr.erl | 49 | ||||
-rw-r--r-- | src/cowboy_protocol.erl | 4 | ||||
-rw-r--r-- | src/cowboy_req.erl | 22 | ||||
-rw-r--r-- | src/cowboy_rest.erl | 6 | ||||
-rw-r--r-- | src/cowboy_websocket.erl | 8 |
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}, |