aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cowboy_protocol.erl14
-rw-r--r--src/cowboy_req.erl68
2 files changed, 65 insertions, 17 deletions
diff --git a/src/cowboy_protocol.erl b/src/cowboy_protocol.erl
index 48c0b00..0e9982b 100644
--- a/src/cowboy_protocol.erl
+++ b/src/cowboy_protocol.erl
@@ -17,6 +17,8 @@
%%
%% The available options are:
%% <dl>
+%% <dt>compress</dt><dd>Whether to automatically compress the response
+%% body when the conditions are met. Disabled by default.</dd>
%% <dt>env</dt><dd>The environment passed and optionally modified
%% by middlewares.</dd>
%% <dt>max_empty_lines</dt><dd>Max number of empty lines before a request.
@@ -64,6 +66,7 @@
socket :: inet:socket(),
transport :: module(),
middlewares :: [module()],
+ compress :: boolean(),
env :: cowboy_middleware:env(),
onrequest :: undefined | onrequest_fun(),
onresponse = undefined :: undefined | onresponse_fun(),
@@ -99,6 +102,7 @@ get_value(Key, Opts, Default) ->
%% @private
-spec init(pid(), inet:socket(), module(), any()) -> ok.
init(ListenerPid, Socket, Transport, Opts) ->
+ Compress = get_value(compress, Opts, false),
MaxEmptyLines = get_value(max_empty_lines, Opts, 5),
MaxHeaderNameLength = get_value(max_header_name_length, Opts, 64),
MaxHeaderValueLength = get_value(max_header_value_length, Opts, 4096),
@@ -112,7 +116,7 @@ init(ListenerPid, Socket, Transport, Opts) ->
Timeout = get_value(timeout, Opts, 5000),
ok = ranch:accept_ack(ListenerPid),
wait_request(<<>>, #state{socket=Socket, transport=Transport,
- middlewares=Middlewares, env=Env,
+ middlewares=Middlewares, compress=Compress, env=Env,
max_empty_lines=MaxEmptyLines, max_keepalive=MaxKeepalive,
max_request_line_length=MaxRequestLineLength,
max_header_name_length=MaxHeaderNameLength,
@@ -457,11 +461,11 @@ parse_host(<< C, Rest/bits >>, Acc) ->
request(Buffer, State=#state{socket=Socket, transport=Transport,
req_keepalive=ReqKeepalive, max_keepalive=MaxKeepalive,
- onresponse=OnResponse},
+ compress=Compress, onresponse=OnResponse},
Method, Path, Query, Fragment, Version, Headers, Host, Port) ->
Req = cowboy_req:new(Socket, Transport, Method, Path, Query, Fragment,
Version, Headers, Host, Port, Buffer, ReqKeepalive < MaxKeepalive,
- OnResponse),
+ Compress, OnResponse),
onrequest(Req, State).
%% Call the global onrequest callback. The callback can send a reply,
@@ -546,13 +550,13 @@ error_terminate(Code, Req, 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,
- onresponse=OnResponse}) ->
+ compress=Compress, onresponse=OnResponse}) ->
receive
{cowboy_req, resp_sent} -> ok
after 0 ->
_ = cowboy_req:reply(Code, cowboy_req:new(Socket, Transport,
<<"GET">>, <<>>, <<>>, <<>>, {1, 1}, [], <<>>, undefined,
- <<>>, false, OnResponse)),
+ <<>>, false, Compress, OnResponse)),
ok
end,
terminate(State).
diff --git a/src/cowboy_req.erl b/src/cowboy_req.erl
index dab9410..973cc65 100644
--- a/src/cowboy_req.erl
+++ b/src/cowboy_req.erl
@@ -42,7 +42,7 @@
-module(cowboy_req).
%% Request API.
--export([new/13]).
+-export([new/14]).
-export([method/1]).
-export([version/1]).
-export([peer/1]).
@@ -156,6 +156,7 @@
buffer = <<>> :: binary(),
%% Response.
+ resp_compress = false :: boolean(),
resp_state = waiting :: locked | waiting | chunks | done,
resp_headers = [] :: cowboy_http:headers(),
resp_body = <<>> :: iodata() | resp_body_fun()
@@ -179,16 +180,16 @@
%% in an optimized way and add the parsed value to p_headers' cache.
-spec new(inet:socket(), module(), binary(), binary(), binary(), binary(),
cowboy_http:version(), cowboy_http:headers(), binary(),
- inet:port_number() | undefined, binary(), boolean(),
+ inet:port_number() | undefined, binary(), boolean(), boolean(),
undefined | cowboy_protocol:onresponse_fun())
-> req().
new(Socket, Transport, Method, Path, Query, Fragment,
Version, Headers, Host, Port, Buffer, CanKeepalive,
- OnResponse) ->
+ Compress, OnResponse) ->
Req = #http_req{socket=Socket, transport=Transport, pid=self(),
method=Method, path=Path, qs=Query, fragment=Fragment, version=Version,
headers=Headers, host=Host, port=Port, buffer=Buffer,
- onresponse=OnResponse},
+ resp_compress=Compress, onresponse=OnResponse},
case CanKeepalive and (Version =:= {1, 1}) of
false ->
Req#http_req{connection=close};
@@ -892,7 +893,8 @@ reply(Status, Headers, Req=#http_req{resp_body=Body}) ->
reply(Status, Headers, Body, Req=#http_req{
socket=Socket, transport=Transport,
version=Version, connection=Connection,
- method=Method, resp_state=waiting, resp_headers=RespHeaders}) ->
+ method=Method, resp_compress=Compress,
+ resp_state=waiting, resp_headers=RespHeaders}) ->
RespConn = response_connection(Headers, Connection),
HTTP11Headers = case Version of
{1, 1} -> [{<<"connection">>, atom_to_connection(Connection)}];
@@ -922,18 +924,60 @@ reply(Status, Headers, Body, Req=#http_req{
BodyFun(Socket, Transport);
true -> ok
end;
+ _ when Compress ->
+ Req2 = reply_may_compress(Status, Headers, Body, Req,
+ RespHeaders, HTTP11Headers, Method);
_ ->
- {_, Req2} = response(Status, Headers, RespHeaders, [
- {<<"content-length">>, integer_to_list(iolist_size(Body))},
- {<<"date">>, cowboy_clock:rfc1123()},
- {<<"server">>, <<"Cowboy">>}
- |HTTP11Headers],
- case Method of <<"HEAD">> -> <<>>; _ -> Body end,
- Req)
+ Req2 = reply_no_compress(Status, Headers, Body, Req,
+ RespHeaders, HTTP11Headers, Method, iolist_size(Body))
end,
{ok, Req2#http_req{connection=RespConn, resp_state=done,
resp_headers=[], resp_body= <<>>}}.
+reply_may_compress(Status, Headers, Body, Req,
+ RespHeaders, HTTP11Headers, Method) ->
+ BodySize = iolist_size(Body),
+ {ok, Encodings, Req2}
+ = cowboy_req:parse_header(<<"accept-encoding">>, Req),
+ CanGzip = (BodySize > 300)
+ andalso (false =:= lists:keyfind(<<"content-encoding">>,
+ 1, Headers))
+ andalso (false =:= lists:keyfind(<<"content-encoding">>,
+ 1, RespHeaders))
+ andalso (false =:= lists:keyfind(<<"transfer-encoding">>,
+ 1, Headers))
+ andalso (false =:= lists:keyfind(<<"transfer-encoding">>,
+ 1, RespHeaders))
+ andalso (Encodings =/= undefined)
+ andalso (false =/= lists:keyfind(<<"gzip">>, 1, Encodings)),
+ case CanGzip of
+ true ->
+ GzBody = zlib:gzip(Body),
+ {_, Req3} = response(Status, Headers, RespHeaders, [
+ {<<"content-length">>, integer_to_list(byte_size(GzBody))},
+ {<<"content-encoding">>, <<"gzip">>},
+ {<<"date">>, cowboy_clock:rfc1123()},
+ {<<"server">>, <<"Cowboy">>}
+ |HTTP11Headers],
+ case Method of <<"HEAD">> -> <<>>; _ -> GzBody end,
+ Req2),
+ Req3;
+ false ->
+ reply_no_compress(Status, Headers, Body, Req,
+ RespHeaders, HTTP11Headers, Method, BodySize)
+ end.
+
+reply_no_compress(Status, Headers, Body, Req,
+ RespHeaders, HTTP11Headers, Method, BodySize) ->
+ {_, Req2} = response(Status, Headers, RespHeaders, [
+ {<<"content-length">>, integer_to_list(BodySize)},
+ {<<"date">>, cowboy_clock:rfc1123()},
+ {<<"server">>, <<"Cowboy">>}
+ |HTTP11Headers],
+ case Method of <<"HEAD">> -> <<>>; _ -> Body end,
+ Req),
+ Req2.
+
%% @equiv chunked_reply(Status, [], Req)
-spec chunked_reply(cowboy_http:status(), Req) -> {ok, Req} when Req::req().
chunked_reply(Status, Req) ->