aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cowboy_req.erl99
-rw-r--r--src/cowboy_rest.erl120
-rw-r--r--src/cowboy_router.erl4
-rw-r--r--src/cowboy_static.erl3
4 files changed, 63 insertions, 163 deletions
diff --git a/src/cowboy_req.erl b/src/cowboy_req.erl
index bdebddd..4ec42f9 100644
--- a/src/cowboy_req.erl
+++ b/src/cowboy_req.erl
@@ -46,7 +46,6 @@
-export([method/1]).
-export([version/1]).
-export([peer/1]).
--export([peer_addr/1]).
-export([host/1]).
-export([host_info/1]).
-export([port/1]).
@@ -230,29 +229,6 @@ version(Req) ->
peer(Req) ->
{Req#http_req.peer, Req}.
-%% @doc Returns the peer address calculated from headers.
--spec peer_addr(Req) -> {inet:ip_address(), Req} when Req::req().
-peer_addr(Req = #http_req{}) ->
- {RealIp, Req1} = header(<<"x-real-ip">>, Req),
- {ForwardedForRaw, Req2} = header(<<"x-forwarded-for">>, Req1),
- {{PeerIp, _PeerPort}, Req3} = peer(Req2),
- ForwardedFor = case ForwardedForRaw of
- undefined ->
- undefined;
- ForwardedForRaw ->
- case re:run(ForwardedForRaw, "^(?<first_ip>[^\\,]+)",
- [{capture, [first_ip], binary}]) of
- {match, [FirstIp]} -> FirstIp;
- _Any -> undefined
- end
- end,
- {ok, PeerAddr} = if
- is_binary(RealIp) -> inet_parse:address(binary_to_list(RealIp));
- is_binary(ForwardedFor) -> inet_parse:address(binary_to_list(ForwardedFor));
- true -> {ok, PeerIp}
- end,
- {PeerAddr, Req3}.
-
%% @doc Return the host binary string.
-spec host(Req) -> {binary(), Req} when Req::req().
host(Req) ->
@@ -426,61 +402,61 @@ parse_header_default(_Name) -> undefined.
-spec parse_header(binary(), Req, any())
-> {ok, any(), Req} | {undefined, binary(), Req}
| {error, badarg} when Req::req().
-parse_header(Name, Req, Default) when Name =:= <<"accept">> ->
+parse_header(Name = <<"accept">>, Req, Default) ->
parse_header(Name, Req, Default,
fun (Value) ->
cowboy_http:list(Value, fun cowboy_http:media_range/2)
end);
-parse_header(Name, Req, Default) when Name =:= <<"accept-charset">> ->
+parse_header(Name = <<"accept-charset">>, Req, Default) ->
parse_header(Name, Req, Default,
fun (Value) ->
cowboy_http:nonempty_list(Value, fun cowboy_http:conneg/2)
end);
-parse_header(Name, Req, Default) when Name =:= <<"accept-encoding">> ->
+parse_header(Name = <<"accept-encoding">>, Req, Default) ->
parse_header(Name, Req, Default,
fun (Value) ->
cowboy_http:list(Value, fun cowboy_http:conneg/2)
end);
-parse_header(Name, Req, Default) when Name =:= <<"accept-language">> ->
+parse_header(Name = <<"accept-language">>, Req, Default) ->
parse_header(Name, Req, Default,
fun (Value) ->
cowboy_http:nonempty_list(Value, fun cowboy_http:language_range/2)
end);
-parse_header(Name, Req, Default) when Name =:= <<"authorization">> ->
+parse_header(Name = <<"authorization">>, Req, Default) ->
parse_header(Name, Req, Default,
fun (Value) ->
cowboy_http:token_ci(Value, fun cowboy_http:authorization/2)
end);
-parse_header(Name, Req, Default) when Name =:= <<"content-length">> ->
+parse_header(Name = <<"content-length">>, Req, Default) ->
parse_header(Name, Req, Default, fun cowboy_http:digits/1);
-parse_header(Name, Req, Default) when Name =:= <<"content-type">> ->
+parse_header(Name = <<"content-type">>, Req, Default) ->
parse_header(Name, Req, Default, fun cowboy_http:content_type/1);
parse_header(Name = <<"cookie">>, Req, Default) ->
parse_header(Name, Req, Default, fun cowboy_http:cookie_list/1);
-parse_header(Name, Req, Default) when Name =:= <<"expect">> ->
+parse_header(Name = <<"expect">>, Req, Default) ->
parse_header(Name, Req, Default,
fun (Value) ->
cowboy_http:nonempty_list(Value, fun cowboy_http:expectation/2)
end);
parse_header(Name, Req, Default)
- when Name =:= <<"if-match">>; Name =:= <<"if-none-match">> ->
+ when Name =:= <<"if-match">>;
+ Name =:= <<"if-none-match">> ->
parse_header(Name, Req, Default, fun cowboy_http:entity_tag_match/1);
parse_header(Name, Req, Default)
when Name =:= <<"if-modified-since">>;
Name =:= <<"if-unmodified-since">> ->
parse_header(Name, Req, Default, fun cowboy_http:http_date/1);
-parse_header(Name, Req, Default) when Name =:= <<"sec-websocket-protocol">> ->
+parse_header(Name, Req, Default)
+ when Name =:= <<"sec-websocket-protocol">>;
+ Name =:= <<"x-forwarded-for">> ->
parse_header(Name, Req, Default,
fun (Value) ->
cowboy_http:nonempty_list(Value, fun cowboy_http:token/2)
end);
%% @todo Extension parameters.
-parse_header(Name, Req, Default) when Name =:= <<"transfer-encoding">> ->
- parse_header(Name, Req, Default,
- fun (Value) ->
- cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2)
- end);
-parse_header(Name, Req, Default) when Name =:= <<"upgrade">> ->
+parse_header(Name, Req, Default)
+ when Name =:= <<"transfer-encoding">>;
+ Name =:= <<"upgrade">> ->
parse_header(Name, Req, Default,
fun (Value) ->
cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2)
@@ -566,7 +542,7 @@ set_meta(Name, Value, Req=#http_req{meta=Meta}) ->
%% Request Body API.
%% @doc Return whether the request message has a body.
--spec has_body(cowboy_req:req()) -> boolean().
+-spec has_body(req()) -> boolean().
has_body(Req) ->
case lists:keyfind(<<"content-length">>, 1, Req#http_req.headers) of
{_, <<"0">>} ->
@@ -613,11 +589,11 @@ init_stream(TransferDecode, TransferState, ContentDecode, Req) ->
{ok, Req#http_req{body_state=
{stream, 0, TransferDecode, TransferState, ContentDecode}}}.
-%% @equiv stream_body(Req, 1000000)
+%% @equiv stream_body(1000000, Req)
-spec stream_body(Req) -> {ok, binary(), Req}
| {done, Req} | {error, atom()} when Req::req().
stream_body(Req) ->
- stream_body(Req, 1000000).
+ stream_body(1000000, Req).
%% @doc Stream the request's body.
%%
@@ -633,10 +609,10 @@ stream_body(Req) ->
%%
%% You can limit the size of the chunks being returned by using the
%% second argument which is the size in bytes. It defaults to 1000000 bytes.
--spec stream_body(Req, non_neg_integer()) -> {ok, binary(), Req}
+-spec stream_body(non_neg_integer(), Req) -> {ok, binary(), Req}
| {done, Req} | {error, atom()} when Req::req().
-stream_body(Req=#http_req{body_state=waiting, version=Version,
- transport=Transport, socket=Socket}, MaxLength) ->
+stream_body(MaxLength, Req=#http_req{body_state=waiting, version=Version,
+ transport=Transport, socket=Socket}) ->
{ok, ExpectHeader, Req1} = parse_header(<<"expect">>, Req),
case ExpectHeader of
[<<"100-continue">>] ->
@@ -648,37 +624,35 @@ stream_body(Req=#http_req{body_state=waiting, version=Version,
end,
case parse_header(<<"transfer-encoding">>, Req1) of
{ok, [<<"chunked">>], Req2} ->
- stream_body(Req2#http_req{body_state=
+ stream_body(MaxLength, Req2#http_req{body_state=
{stream, 0,
fun cowboy_http:te_chunked/2, {0, 0},
- fun cowboy_http:ce_identity/1}},
- MaxLength);
+ fun cowboy_http:ce_identity/1}});
{ok, [<<"identity">>], Req2} ->
{Length, Req3} = body_length(Req2),
case Length of
0 ->
{done, Req3#http_req{body_state=done}};
Length ->
- stream_body(Req3#http_req{body_state=
+ stream_body(MaxLength, Req3#http_req{body_state=
{stream, Length,
fun cowboy_http:te_identity/2, {0, Length},
- fun cowboy_http:ce_identity/1}},
- MaxLength)
+ fun cowboy_http:ce_identity/1}})
end
end;
-stream_body(Req=#http_req{body_state=done}, _) ->
+stream_body(_, Req=#http_req{body_state=done}) ->
{done, Req};
-stream_body(Req=#http_req{buffer=Buffer}, _)
+stream_body(_, Req=#http_req{buffer=Buffer})
when Buffer =/= <<>> ->
transfer_decode(Buffer, Req#http_req{buffer= <<>>});
-stream_body(Req, MaxLength) ->
- stream_body_recv(Req, MaxLength).
+stream_body(MaxLength, Req) ->
+ stream_body_recv(MaxLength, Req).
--spec stream_body_recv(Req, non_neg_integer())
+-spec stream_body_recv(non_neg_integer(), Req)
-> {ok, binary(), Req} | {error, atom()} when Req::req().
-stream_body_recv(Req=#http_req{
+stream_body_recv(MaxLength, Req=#http_req{
transport=Transport, socket=Socket, buffer=Buffer,
- body_state={stream, Length, _, _, _}}, MaxLength) ->
+ body_state={stream, Length, _, _, _}}) ->
%% @todo Allow configuring the timeout.
case Transport:recv(Socket, min(Length, MaxLength), 5000) of
{ok, Data} -> transfer_decode(<< Buffer/binary, Data/binary >>,
@@ -697,8 +671,8 @@ transfer_decode(Data, Req=#http_req{body_state={stream, _,
TransferDecode, TransferState2, ContentDecode}});
%% @todo {header(s) for chunked
more ->
- stream_body_recv(Req#http_req{buffer=Data, body_state={stream,
- 0, TransferDecode, TransferState, ContentDecode}}, 0);
+ stream_body_recv(0, Req#http_req{buffer=Data, body_state={stream,
+ 0, TransferDecode, TransferState, ContentDecode}});
{more, Length, Data2, TransferState2} ->
content_decode(ContentDecode, Data2,
Req#http_req{body_state={stream, Length,
@@ -1015,8 +989,7 @@ reply(Status, Headers, Body, Req=#http_req{
reply_may_compress(Status, Headers, Body, Req,
RespHeaders, HTTP11Headers, Method) ->
BodySize = iolist_size(Body),
- {ok, Encodings, Req2}
- = cowboy_req:parse_header(<<"accept-encoding">>, Req),
+ {ok, Encodings, Req2} = parse_header(<<"accept-encoding">>, Req),
CanGzip = (BodySize > 300)
andalso (false =:= lists:keyfind(<<"content-encoding">>,
1, Headers))
diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl
index cb4fffb..526f102 100644
--- a/src/cowboy_rest.erl
+++ b/src/cowboy_rest.erl
@@ -96,9 +96,8 @@ known_methods(Req, State=#state{method=Method}) ->
case call(Req, State, known_methods) of
no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">>;
Method =:= <<"POST">>; Method =:= <<"PUT">>;
- Method =:= <<"DELETE">>; Method =:= <<"TRACE">>;
- Method =:= <<"CONNECT">>; Method =:= <<"OPTIONS">>;
- Method =:= <<"PATCH">> ->
+ Method =:= <<"PATCH">>; Method =:= <<"DELETE">>;
+ Method =:= <<"OPTIONS">> ->
next(Req, State, fun uri_too_long/2);
no_call ->
next(Req, State, 501);
@@ -236,8 +235,8 @@ content_types_provided(Req, State) ->
normalize_content_types({ContentType, Callback})
when is_binary(ContentType) ->
{cowboy_http:content_type(ContentType), Callback};
-normalize_content_types(Provided) ->
- Provided.
+normalize_content_types(Normalized) ->
+ Normalized.
prioritize_accept(Accept) ->
lists:sort(
@@ -728,16 +727,18 @@ is_post_to_missing_resource(Req, State, OnFalse) ->
respond(Req, State, OnFalse).
allow_missing_post(Req, State, OnFalse) ->
- expect(Req, State, allow_missing_post, true, fun post_is_create/2, OnFalse).
+ expect(Req, State, allow_missing_post, true, fun post_resource/2, OnFalse).
+
+post_resource(Req, State) ->
+ accept_resource(Req, State, 204).
method(Req, State=#state{method= <<"DELETE">>}) ->
delete_resource(Req, State);
-method(Req, State=#state{method= <<"POST">>}) ->
- post_is_create(Req, State);
method(Req, State=#state{method= <<"PUT">>}) ->
is_conflict(Req, State);
-method(Req, State=#state{method= <<"PATCH">>}) ->
- patch_resource(Req, State);
+method(Req, State=#state{method=Method})
+ when Method =:= <<"POST">>; Method =:= <<"PATCH">> ->
+ accept_resource(Req, State, 204);
method(Req, State=#state{method=Method})
when Method =:= <<"GET">>; Method =:= <<"HEAD">> ->
set_resp_body_etag(Req, State);
@@ -752,79 +753,21 @@ delete_resource(Req, State) ->
delete_completed(Req, State) ->
expect(Req, State, delete_completed, true, fun has_resp_body/2, 202).
-%% post_is_create/2 indicates whether the POST method can create new resources.
-post_is_create(Req, State) ->
- expect(Req, State, post_is_create, false, fun process_post/2, fun create_path/2).
-
-%% When the POST method can create new resources, create_path/2 will be called
-%% and is expected to return the full path to the new resource
-%% (including the leading /).
-create_path(Req, State) ->
- case call(Req, State, create_path) of
- no_call ->
- put_resource(Req, State, fun created_path/2);
- {halt, Req2, HandlerState} ->
- terminate(Req2, State#state{handler_state=HandlerState});
- {Path, Req2, HandlerState} ->
- {HostURL, Req3} = cowboy_req:host_url(Req2),
- State2 = State#state{handler_state=HandlerState},
- Req4 = cowboy_req:set_resp_header(
- <<"location">>, << HostURL/binary, Path/binary >>, Req3),
- put_resource(cowboy_req:set_meta(put_path, Path, Req4),
- State2, 303)
- end.
-
-%% Called after content_types_accepted is called for POST methods
-%% when create_path did not exist. Expects the full path to
-%% be returned and MUST exist in the case that create_path
-%% does not.
-created_path(Req, State) ->
- case call(Req, State, created_path) of
- {halt, Req2, HandlerState} ->
- terminate(Req2, State#state{handler_state=HandlerState});
- {Path, Req2, HandlerState} ->
- {HostURL, Req3} = cowboy_req:host_url(Req2),
- State2 = State#state{handler_state=HandlerState},
- Req4 = cowboy_req:set_resp_header(
- <<"location">>, << HostURL/binary, Path/binary >>, Req3),
- respond(cowboy_req:set_meta(put_path, Path, Req4),
- State2, 303)
- end.
-
-%% process_post should return true when the POST body could be processed
-%% and false when it hasn't, in which case a 500 error is sent.
-process_post(Req, State) ->
- case call(Req, State, process_post) of
- {halt, Req2, HandlerState} ->
- terminate(Req2, State#state{handler_state=HandlerState});
- {true, Req2, HandlerState} ->
- State2 = State#state{handler_state=HandlerState},
- next(Req2, State2, fun is_new_resource/2);
- {false, Req2, HandlerState} ->
- State2 = State#state{handler_state=HandlerState},
- respond(Req2, State2, 500)
- end.
-
is_conflict(Req, State) ->
expect(Req, State, is_conflict, false, fun put_resource/2, 409).
put_resource(Req, State) ->
- Path = cowboy_req:get(path, Req),
- put_resource(cowboy_req:set_meta(put_path, Path, Req),
- State, fun is_new_resource/2).
+ accept_resource(Req, State, fun is_new_resource/2).
%% content_types_accepted should return a list of media types and their
%% associated callback functions in the same format as content_types_provided.
%%
%% The callback will then be called and is expected to process the content
-%% pushed to the resource in the request body. The path to the new resource
-%% may be different from the request path, and is stored as request metadata.
-%% It is always defined past this point. It can be retrieved as demonstrated:
-%% {PutPath, Req2} = cowboy_req:meta(put_path, Req)
+%% pushed to the resource in the request body.
%%
-%%content_types_accepted SHOULD return a different list
+%% content_types_accepted SHOULD return a different list
%% for each HTTP method.
-put_resource(Req, State, OnTrue) ->
+accept_resource(Req, State, OnTrue) ->
case call(Req, State, content_types_accepted) of
no_call ->
respond(Req, State, 415);
@@ -838,27 +781,6 @@ put_resource(Req, State, OnTrue) ->
choose_content_type(Req3, State2, OnTrue, ContentType, CTA2)
end.
-%% content_types_accepted should return a list of media types and their
-%% associated callback functions in the same format as content_types_provided.
-%%
-%% The callback will then be called and is expected to process the content
-%% pushed to the resource in the request body.
-%%
-%% content_types_accepted SHOULD return a different list
-%% for each HTTP method.
-patch_resource(Req, State) ->
- case call(Req, State, content_types_accepted) of
- no_call ->
- respond(Req, State, 415);
- {halt, Req2, HandlerState} ->
- terminate(Req2, State#state{handler_state=HandlerState});
- {CTM, Req2, HandlerState} ->
- State2 = State#state{handler_state=HandlerState},
- {ok, ContentType, Req3}
- = cowboy_req:parse_header(<<"content-type">>, Req2),
- choose_content_type(Req3, State2, 204, ContentType, CTM)
- end.
-
%% The special content type '*' will always match. It can be used as a
%% catch-all content type for accepting any kind of request content.
%% Note that because it will always match, it should be the last of the
@@ -880,9 +802,8 @@ choose_content_type(Req, State, OnTrue,
choose_content_type(Req, State, OnTrue, ContentType, [_Any|Tail]) ->
choose_content_type(Req, State, OnTrue, ContentType, Tail).
-process_content_type(Req,
- State=#state{handler=Handler, handler_state=HandlerState},
- OnTrue, Fun) ->
+process_content_type(Req, State=#state{method=Method,
+ handler=Handler, handler_state=HandlerState}, OnTrue, Fun) ->
case call(Req, State, Fun) of
no_call ->
error_logger:error_msg(
@@ -898,7 +819,12 @@ process_content_type(Req,
next(Req2, State2, OnTrue);
{false, Req2, HandlerState2} ->
State2 = State#state{handler_state=HandlerState2},
- respond(Req2, State2, 422)
+ respond(Req2, State2, 422);
+ {ResURL, Req2, HandlerState2} when Method =:= <<"POST">> ->
+ State2 = State#state{handler_state=HandlerState2},
+ Req3 = cowboy_req:set_resp_header(
+ <<"location">>, ResURL, Req2),
+ respond(Req3, State2, 303)
end.
%% Whether we created a new resource, either through PUT or POST.
diff --git a/src/cowboy_router.erl b/src/cowboy_router.erl
index 7c86653..91912d8 100644
--- a/src/cowboy_router.erl
+++ b/src/cowboy_router.erl
@@ -37,7 +37,7 @@
| {atom(), function, fun ((binary()) -> true | {true, any()} | false)}].
-export_type([constraints/0]).
--type route_match() :: '_' | binary() | string().
+-type route_match() :: '_' | iodata().
-type route_path() :: {Path::route_match(), Handler::module(), Opts::any()}
| {Path::route_match(), constraints(), Handler::module(), Opts::any()}.
-type route_rule() :: {Host::route_match(), Paths::[route_path()]}
@@ -88,7 +88,7 @@ compile_paths([{PathMatch, Handler, Opts}|Tail], Acc) ->
compile_paths([{PathMatch, [], Handler, Opts}|Tail], Acc);
compile_paths([{PathMatch, Constraints, Handler, Opts}|Tail], Acc)
when is_list(PathMatch) ->
- compile_paths([{list_to_binary(PathMatch),
+ compile_paths([{iolist_to_binary(PathMatch),
Constraints, Handler, Opts}|Tail], Acc);
compile_paths([{'_', Constraints, Handler, Opts}|Tail], Acc) ->
compile_paths(Tail, [{'_', Constraints, Handler, Opts}] ++ Acc);
diff --git a/src/cowboy_static.erl b/src/cowboy_static.erl
index d583fa9..fe6600c 100644
--- a/src/cowboy_static.erl
+++ b/src/cowboy_static.erl
@@ -324,7 +324,8 @@ file_contents(Req, #state{filepath=Filepath,
%% if the connection is closed while sending the file.
case Transport:sendfile(Socket, Filepath) of
{ok, _} -> ok;
- {error, closed} -> ok
+ {error, closed} -> ok;
+ {error, etimedout} -> ok
end
end,
{{stream, Filesize, Writefile}, Req, State}.