aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2019-10-05 17:32:50 +0200
committerLoïc Hoguin <[email protected]>2019-10-05 17:32:50 +0200
commit3e23aff1d1b6e4e2f736edd7a8f5465b02c4c6db (patch)
treeaa15ba2ab858e52be86250ddb7637466252e3bda
parentc50d6aa09c9028dca3365516d30f1242cfd43306 (diff)
downloadcowboy-3e23aff1d1b6e4e2f736edd7a8f5465b02c4c6db.tar.gz
cowboy-3e23aff1d1b6e4e2f736edd7a8f5465b02c4c6db.tar.bz2
cowboy-3e23aff1d1b6e4e2f736edd7a8f5465b02c4c6db.zip
Add Websocket option validate_utf8
This allows disabling the UTF-8 validation check for text and close frames.
-rw-r--r--doc/src/manual/cowboy_websocket.asciidoc22
-rw-r--r--src/cowboy_websocket.erl11
-rw-r--r--test/handlers/ws_dont_validate_utf8_h.erl23
-rw-r--r--test/ws_SUITE.erl13
4 files changed, 61 insertions, 8 deletions
diff --git a/doc/src/manual/cowboy_websocket.asciidoc b/doc/src/manual/cowboy_websocket.asciidoc
index f8038e5..2f01b8a 100644
--- a/doc/src/manual/cowboy_websocket.asciidoc
+++ b/doc/src/manual/cowboy_websocket.asciidoc
@@ -151,11 +151,12 @@ Cowboy does it automatically for you.
[source,erlang]
----
opts() :: #{
- compress => boolean(),
- deflate_opts => cow_ws:deflate_opts()
- idle_timeout => timeout(),
+ compress => boolean(),
+ deflate_opts => cow_ws:deflate_opts()
+ idle_timeout => timeout(),
max_frame_size => non_neg_integer() | infinity,
- req_filter => fun((cowboy_req:req()) -> map())
+ req_filter => fun((cowboy_req:req()) -> map()),
+ validate_utf8 => boolean()
}
----
@@ -209,8 +210,21 @@ given back in the `terminate/3` callback. By default
it keeps the method, version, URI components and peer
information.
+validate_utf8 (true)::
+
+Whether Cowboy should verify that the payload of
+`text` and `close` frames is valid UTF-8. This is
+required by the protocol specification but in some
+cases it may be more interesting to disable it in
+order to save resources.
++
+Note that `binary` frames do not have this UTF-8
+requirement and are what should be used under
+normal circumstances if necessary.
+
== Changelog
+* *2.7*: The option `validate_utf8` has been added.
* *2.6*: Deflate options can now be configured via `deflate_opts`.
* *2.0*: The Req object is no longer passed to Websocket callbacks.
* *2.0*: The callback `websocket_terminate/3` was removed in favor of `terminate/3`.
diff --git a/src/cowboy_websocket.erl b/src/cowboy_websocket.erl
index 5cc061a..ad0dad5 100644
--- a/src/cowboy_websocket.erl
+++ b/src/cowboy_websocket.erl
@@ -73,7 +73,8 @@
deflate_opts => cow_ws:deflate_opts(),
idle_timeout => timeout(),
max_frame_size => non_neg_integer() | infinity,
- req_filter => fun((cowboy_req:req()) -> map())
+ req_filter => fun((cowboy_req:req()) -> map()),
+ validate_utf8 => boolean()
}.
-export_type([opts/0]).
@@ -91,7 +92,7 @@
hibernate = false :: boolean(),
frag_state = undefined :: cow_ws:frag_state(),
frag_buffer = <<>> :: binary(),
- utf8_state = 0 :: cow_ws:utf8_state(),
+ utf8_state :: cow_ws:utf8_state(),
deflate = true :: boolean(),
extensions = #{} :: map(),
req = #{} :: map()
@@ -133,7 +134,11 @@ upgrade(Req0=#{version := Version}, Env, Handler, HandlerState, Opts) ->
undefined -> maps:with([method, version, scheme, host, port, path, qs, peer], Req0);
FilterFun -> FilterFun(Req0)
end,
- State0 = #state{opts=Opts, handler=Handler, req=FilteredReq},
+ Utf8State = case maps:get(validate_utf8, Opts, true) of
+ true -> 0;
+ false -> undefined
+ end,
+ State0 = #state{opts=Opts, handler=Handler, utf8_state=Utf8State, req=FilteredReq},
try websocket_upgrade(State0, Req0) of
{ok, State, Req} ->
websocket_handshake(State, Req, HandlerState, Env);
diff --git a/test/handlers/ws_dont_validate_utf8_h.erl b/test/handlers/ws_dont_validate_utf8_h.erl
new file mode 100644
index 0000000..6599c78
--- /dev/null
+++ b/test/handlers/ws_dont_validate_utf8_h.erl
@@ -0,0 +1,23 @@
+%% This module disables UTF-8 validation.
+
+-module(ws_dont_validate_utf8_h).
+-behavior(cowboy_websocket).
+
+-export([init/2]).
+-export([websocket_handle/2]).
+-export([websocket_info/2]).
+
+init(Req, State) ->
+ {cowboy_websocket, Req, State, #{
+ validate_utf8 => false
+ }}.
+
+websocket_handle({text, Data}, State) ->
+ {reply, {text, Data}, State};
+websocket_handle({binary, Data}, State) ->
+ {reply, {binary, Data}, State};
+websocket_handle(_, State) ->
+ {ok, State}.
+
+websocket_info(_, State) ->
+ {ok, State}.
diff --git a/test/ws_SUITE.erl b/test/ws_SUITE.erl
index c99830c..64b8561 100644
--- a/test/ws_SUITE.erl
+++ b/test/ws_SUITE.erl
@@ -67,7 +67,8 @@ init_dispatch() ->
{"/ws_timeout_hibernate", ws_timeout_hibernate, []},
{"/ws_timeout_cancel", ws_timeout_cancel, []},
{"/ws_max_frame_size", ws_max_frame_size, []},
- {"/ws_deflate_opts", ws_deflate_opts_h, []}
+ {"/ws_deflate_opts", ws_deflate_opts_h, []},
+ {"/ws_dont_validate_utf8", ws_dont_validate_utf8_h, []}
]}
]).
@@ -304,6 +305,16 @@ do_ws_deflate_opts_z(Path, Config) ->
{error, closed} = gen_tcp:recv(Socket, 0, 6000),
ok.
+ws_dont_validate_utf8(Config) ->
+ doc("Handler is configured with UTF-8 validation disabled."),
+ {ok, Socket, _} = do_handshake("/ws_dont_validate_utf8", Config),
+ %% Send an invalid UTF-8 text frame and receive it back.
+ Mask = 16#37fa213d,
+ MaskedInvalid = do_mask(<<255, 255, 255, 255>>, Mask, <<>>),
+ ok = gen_tcp:send(Socket, <<1:1, 0:3, 1:4, 1:1, 4:7, Mask:32, MaskedInvalid/binary>>),
+ {ok, <<1:1, 0:3, 1:4, 0:1, 4:7, 255, 255, 255, 255>>} = gen_tcp:recv(Socket, 0, 6000),
+ ok.
+
ws_first_frame_with_handshake(Config) ->
doc("Client sends the first frame immediately with the handshake. "
"This is invalid according to the protocol but we still want "