Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 14 commits
  • 34 files changed
  • 0 commit comments
  • 6 contributors
Commits on Apr 08, 2012
@asabil asabil Make charsets parsing more relaxed
Certain user agents send invalid Accept-Charset headers, like the
following: "ISO-8859-1;utf-8;q=0.7,*;q=0.7"

The user agent with which this behavior was observed presented itself
with the User-Agent string: "Mozilla/5.0 (Windows; U; Windows NT 5.1;
en-US; rv:1.9) Gecko/2008052906 Firefox/3.0" Although this doesn't
appear to be correct. The request might have been mangled by a
transparent proxy.
4b43d06
Commits on Apr 30, 2012
Magnus Klaar Add file option to cowboy_http_static 8168ae9
Commits on May 06, 2012
@nox nox Add new HTTP status codes from RFC 6585
See http://tools.ietf.org/html/rfc6585 for further details.
de89550
Commits on May 14, 2012
@essen essen Merge pull request #216 from nox/rfc6585
Add new HTTP status codes from RFC 6585
4f78be6
@bfrog bfrog Only ignore slashes in cookie values for the path
* Ensures cookie encoding errors are caught earlier
* Fix separator misspelling
7ffd324
Commits on May 21, 2012
Loïc Hoguin Small updates to the ROADMAP and doc comments 8363e89
Loïc Hoguin Make multipart code use stream_body ca9278b
Loïc Hoguin Fix a bug preventing 'onresponse' from being called on errors cc6c4e3
Loïc Hoguin Merge branch 'fix/relax-conneg-parsing' of https://github.com/tillite… 040c6dc
Loïc Hoguin Merge branch 'serve-static-file' of https://github.com/klaar/cowboy
Fix alphabetical order since @klaar seems to have issues with it. ;)
295dc64
Loïc Hoguin Merge branch 'cookie_slash' of https://github.com/bfrog/cowboy a95245d
Loïc Hoguin Remove a dumb warning when running the tests 1a1b01c
Commits on May 23, 2012
Loïc Hoguin Update version to 0.6.0
Also update the CHANGELOG and copyright years.
0c2e222
Commits on May 29, 2012
Ryosuke Nakai Merge remote-tracking branch 'upstream/master' 22b05fe
View
86 CHANGELOG.md
@@ -1,6 +1,92 @@
CHANGELOG
=========
+0.6.0
+-----
+
+* Add multipart support
+
+* Add chunked transfer decoding support
+
+ Done by reworking the body reading API. Now all the body
+ reading goes through the cowboy_http_req:stream_body/1
+ function. This function takes care of handling both the
+ Transfer-Encoding and the Content-Encoding, returning
+ properly decoded data ready for consumption.
+
+* Add fragmented websocket messages support
+
+ Properly tested by the addition of the Autobahn websocket
+ test suite to our toolbox. All tests pass except a few
+ related to UTF-8 handling, as Cowboy does no checks on that
+ end at this point.
+
+* Add 'onrequest' and 'onresponse' hooks
+
+ The first can be used for all the special cases you may have
+ that can't be dealt with otherwise. It's also pretty good for
+ writing access logs or rewriting URLs.
+
+ The second can be used for logging errors or replacing error
+ pages, amongst others.
+
+* Add cowboy:get_protocol_options/1 and cowboy:set_protocol_options/2
+
+ These functions allow for retrieving a listener's protocol options,
+ and for modifying them while the listener is running. This is
+ most useful to upgrade the dispatch list. The upgrade applies
+ to all the future connections.
+
+* Add the sockname/1 function to TCP and SSL transports
+
+* Improve SSL transport support
+
+ Add support for specifying the ciphers. Add CA support. Make
+ specifying the password optional.
+
+* Add new HTTP status codes from RFC 6585
+
+* Add a 'file' option to cowboy_http_static
+
+ This allows for mapping /folder/ paths to a /folder/index.html file.
+
+* Add the '*' catch all Content-Type for REST
+
+* Add {halt, Req, State} as a possible return value for REST
+
+* Add absolute URI support for requests
+
+* Add cowboy_http:x_www_form_urlencoded/2
+
+* Various REST bug fixes
+
+* Do not send chunked replies for HTTP/1.0 connections
+
+* Fix a DST bug in the cookies code
+
+* Fix a bug with setting cookie values containing slashes
+
+* Fix a small timer leak when using loop/websocket timeouts
+
+* Make charset and media type parsing more relaxed
+
+ This is to accomodate some widely used broken clients.
+
+* Make error messages more readable
+
+* Fix and improve type specifications
+
+* Fix a bug preventing documentation from being generated
+
+* Small improvements to the documentation
+
+* Rework the HTTP test suite
+
+ The suite now uses an integrated Cowboy HTTP client. The client
+ is currently experimental and shouldn't be used.
+
+* Add many many tests.
+
0.4.0
-----
View
2 LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
View
14 README.md
@@ -157,28 +157,28 @@ based on the hostname and path information from the request. It also lets
you define static options for the handler directly in the rules.
To match the hostname and path, Cowboy requires a list of tokens. For
-example, to match the "dev-extend.eu" domain name, you must specify
-`[<<"dev-extend">>, <<"eu">>]`. Or, to match the "/path/to/my/resource"
+example, to match the "ninenines.eu" domain name, you must specify
+`[<<"ninenines">>, <<"eu">>]`. Or, to match the "/path/to/my/resource"
you must use `[<<"path">>, <<"to">>, <<"my">>, <<"resource">>]`. All the
tokens must be given as binary.
You can use the special token `'_'` (the atom underscore) to indicate that
you accept anything in that position. For example if you have both
-"dev-extend.eu" and "dev-extend.fr" domains, you can use the match spec
-`[<<"dev-extend">>, '_']` to match any top level extension.
+"ninenines.eu" and "ninenines.fr" domains, you can use the match spec
+`[<<"ninenines">>, '_']` to match any top level extension.
Finally, you can also match multiple leading segments of the domain name and
multiple trailing segments of the request path using the atom `'...'` (the atom
ellipsis) respectively as the first host token or the last path token. For
-example, host rule `['...', <<"dev-extend">>, <<"eu">>]` can match both
-"cowboy.bugs.dev-extend.eu" and "dev-extend.eu" and path rule
+example, host rule `['...', <<"ninenines">>, <<"eu">>]` can match both
+"cowboy.bugs.ninenines.eu" and "ninenines.eu" and path rule
`[<<"projects">>, '...']` can match both "/projects" and
"/projects/cowboy/issues/42". The host leading segments and the path trailing
segments can later be retrieved through `cowboy_http_req:host_info/1` and
`cowboy_http_req:path_info/1`.
Any other atom used as a token will bind the value to this atom when
-matching. To follow on our hostnames example, `[<<"dev-extend">>, ext]`
+matching. To follow on our hostnames example, `[<<"ninenines">>, ext]`
would bind the values `<<"eu">>` and `<<"fr">>` to the ext atom, that you
can later retrieve in your handler by calling `cowboy_http_req:binding/{2,3}`.
View
40 ROADMAP.md
@@ -61,56 +61,18 @@ are not ordered.
Tools like curl expect a 100 Continue before sending a
request body by default.
-* Content-Encoding support.
-
- Cowboy should be able to send encoded content automatically.
- The default should be to encode, but the developer must be
- able to override this default either for the whole listener
- or just for a single reply.
-
-* Improve body reading API.
-
- We want to have various different things for reading the
- body. First, there should be raw functions for the different
- ways to read the body: basic, transfer encoded, multipart.
- Each should allow us to limit the size of what is read.
-
- On top of these functions there should be two more
- advanced functions: one would return the result of parsing
- a x-www-form-urlencoded body; the other would parse a
- multipart request, save files from the multipart data to
- a temporary location and return a proplist of values if any
- along with the files details. This behavior is similar to
- what is done automatically by PHP with its $_FILES array.
-
- The advanced functions are of course here for convenience
- only and it should be trivial to reimplement them directly
- in a Cowboy application if needed.
-
* Complete the work on Websockets.
Now that the Autobahn test suite is available (make inttests),
we have a definite way to know whether Cowboy's implementation
of Websockets is right. The work can thus be completed. The
- remaining tasks are proper UTF8 handling and fragmentation.
+ remaining task is proper UTF8 handling.
* SPDY support.
While SPDY probably won't be added directly to Cowboy, work
has been started on making Cowboy use SPDY.
-* Hooks.
-
- Customizable hooks would allow the developer to extend Cowboy
- easily. Two kinds of hooks are needed: before dispatching the
- request, and before sending a reply.
-
- The first would allow us to apply site-wide functions like
- authentication or request logging and modify the Req if needed.
-
- The second is more interesting for response logging or to
- filter the replies, for example to send custom error pages.
-
* Transport upgrades.
Some protocols allow an upgrade from TCP to SSL without
View
4 doc/overview.edoc
@@ -1,4 +1,4 @@
-@author Lo�c Hoguin <essen@dev-extend.eu>
-@copyright 2011 Lo�c Hoguin
+@author Lo�c Hoguin <essen@ninenines.eu>
+@copyright 2011-2012 Lo�c Hoguin
@version HEAD
@title Small, fast, modular HTTP server.
View
2 include/http.hrl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
View
4 src/cowboy.app.src
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,7 @@
{application, cowboy, [
{description, "Small, fast, modular HTTP server."},
- {vsn, "0.5.0"},
+ {vsn, "0.6.0"},
{modules, []},
{registered, [cowboy_clock, cowboy_sup]},
{applications, [
View
3 src/cowboy.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
@@ -51,7 +51,6 @@ start_listener(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts)
Transport, TransOpts, Protocol, ProtoOpts)).
%% @doc Stop a listener identified by <em>Ref</em>.
-%% @todo Currently request processes aren't terminated with the listener.
-spec stop_listener(any()) -> ok | {error, not_found}.
stop_listener(Ref) ->
case supervisor:terminate_child(cowboy_sup, {cowboy_listener_sup, Ref}) of
View
2 src/cowboy_acceptor.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 src/cowboy_acceptors_sup.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 src/cowboy_app.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 src/cowboy_bstr.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 src/cowboy_clock.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
34 src/cowboy_cookies.erl
@@ -96,7 +96,7 @@ cookie(Key, Value, Options) when is_binary(Key)
undefined ->
<<"">>;
Path ->
- <<"; Path=", (quote(Path))/binary>>
+ <<"; Path=", (quote(Path, true))/binary>>
end,
HttpOnlyPart =
case proplists:get_value(http_only, Options) of
@@ -119,7 +119,7 @@ is_whitespace($\r) -> true;
is_whitespace($\n) -> true;
is_whitespace(_) -> false.
-%% @doc Check if a character is a seperator.
+%% @doc Check if a character is a separator.
-spec is_separator(char()) -> boolean().
is_separator(C) when C < 32 -> true;
is_separator($\s) -> true;
@@ -143,34 +143,39 @@ is_separator(${) -> true;
is_separator($}) -> true;
is_separator(_) -> false.
-%% @doc Check if a binary has an ASCII seperator character.
--spec has_seperator(binary()) -> boolean().
-has_seperator(<<>>) ->
+%% @doc Check if a binary has an ASCII separator character.
+-spec has_separator(binary(), boolean()) -> boolean().
+has_separator(<<>>, _) ->
false;
-has_seperator(<<$/, Rest/binary>>) ->
- has_seperator(Rest);
-has_seperator(<<C, Rest/binary>>) ->
+has_separator(<<$/, Rest/binary>>, true) ->
+ has_separator(Rest, true);
+has_separator(<<C, Rest/binary>>, IgnoreSlash) ->
case is_separator(C) of
true ->
true;
false ->
- has_seperator(Rest)
+ has_separator(Rest, IgnoreSlash)
end.
%% @doc Convert to a binary and raise an error if quoting is required. Quoting
%% is broken in different ways for different browsers. Its better to simply
%% avoiding doing it at all.
%% @end
--spec quote(term()) -> binary().
-quote(V0) ->
+-spec quote(term(), boolean()) -> binary().
+quote(V0, IgnoreSlash) ->
V = any_to_binary(V0),
- case has_seperator(V) of
+ case has_separator(V, IgnoreSlash) of
true ->
erlang:error({cookie_quoting_required, V});
false ->
V
end.
+%% @equiv quote(Bin, false)
+-spec quote(term()) -> binary().
+quote(V0) ->
+ quote(V0, false).
+
-spec add_seconds(integer(), calendar:datetime()) -> calendar:datetime().
add_seconds(Secs, LocalTime) ->
Greg = calendar:datetime_to_gregorian_seconds(LocalTime),
@@ -265,7 +270,7 @@ binary_splitwith(F, Head, Tail) ->
binary_splitwith(F, String) ->
binary_splitwith(F, <<>>, String).
-%% @doc Split the binary when the next seperator is found.
+%% @doc Split the binary when the next separator is found.
-spec read_token(binary()) -> {binary(), binary()}.
read_token(String) ->
binary_splitwith(fun is_separator/1, String).
@@ -301,6 +306,9 @@ quote_test() ->
catch error:{cookie_quoting_required, <<":wq">>} -> ok
end,
?assertEqual(<<"foo">>,quote(foo)),
+ _ = try quote(<<"/test/slashes/">>)
+ catch error:{cookie_quoting_required, <<"/test/slashes/">>} -> ok
+ end,
ok.
parse_cookie_test() ->
View
76 src/cowboy_dispatcher.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
@@ -85,8 +85,8 @@ do_split_path(RawPath, Separator, URLDec) ->
%% corresponding token value and return it.
%%
%% The list of hostname tokens is reversed before matching. For example, if
-%% we were to match "www.dev-extend.eu", we would first match "eu", then
-%% "dev-extend", then "www". This means that in the context of hostnames,
+%% we were to match "www.ninenines.eu", we would first match "eu", then
+%% "ninenines", then "www". This means that in the context of hostnames,
%% the <em>'...'</em> atom matches properly the lower levels of the domain
%% as would be expected.
%%
@@ -173,16 +173,16 @@ split_host_test_() ->
{<<"">>, {[], <<"">>, undefined}},
{<<".........">>, {[], <<".........">>, undefined}},
{<<"*">>, {[<<"*">>], <<"*">>, undefined}},
- {<<"cowboy.dev-extend.eu">>,
- {[<<"cowboy">>, <<"dev-extend">>, <<"eu">>],
- <<"cowboy.dev-extend.eu">>, undefined}},
- {<<"dev-extend..eu">>,
- {[<<"dev-extend">>, <<>>, <<"eu">>],
- <<"dev-extend..eu">>, undefined}},
- {<<"dev-extend.eu">>,
- {[<<"dev-extend">>, <<"eu">>], <<"dev-extend.eu">>, undefined}},
- {<<"dev-extend.eu:8080">>,
- {[<<"dev-extend">>, <<"eu">>], <<"dev-extend.eu">>, 8080}},
+ {<<"cowboy.ninenines.eu">>,
+ {[<<"cowboy">>, <<"ninenines">>, <<"eu">>],
+ <<"cowboy.ninenines.eu">>, undefined}},
+ {<<"ninenines..eu">>,
+ {[<<"ninenines">>, <<>>, <<"eu">>],
+ <<"ninenines..eu">>, undefined}},
+ {<<"ninenines.eu">>,
+ {[<<"ninenines">>, <<"eu">>], <<"ninenines.eu">>, undefined}},
+ {<<"ninenines.eu:8080">>,
+ {[<<"ninenines">>, <<"eu">>], <<"ninenines.eu">>, 8080}},
{<<"a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z">>,
{[<<"a">>, <<"b">>, <<"c">>, <<"d">>, <<"e">>, <<"f">>, <<"g">>,
<<"h">>, <<"i">>, <<"j">>, <<"k">>, <<"l">>, <<"m">>, <<"n">>,
@@ -195,13 +195,13 @@ split_host_test_() ->
split_host_fail_test_() ->
Tests = [
- <<"dev-extend.eu:owns">>,
- <<"dev-extend.eu: owns">>,
- <<"dev-extend.eu:42fun">>,
- <<"dev-extend.eu: 42fun">>,
- <<"dev-extend.eu:42 fun">>,
- <<"dev-extend.eu:fun 42">>,
- <<"dev-extend.eu: 42">>,
+ <<"ninenines.eu:owns">>,
+ <<"ninenines.eu: owns">>,
+ <<"ninenines.eu:42fun">>,
+ <<"ninenines.eu: 42fun">>,
+ <<"ninenines.eu:42 fun">>,
+ <<"ninenines.eu:fun 42">>,
+ <<"ninenines.eu: 42">>,
<<":owns">>,
<<":42 fun">>
],
@@ -233,14 +233,14 @@ split_path_test_() ->
match_test_() ->
Dispatch = [
- {[<<"www">>, '_', <<"dev-extend">>, <<"eu">>], [
+ {[<<"www">>, '_', <<"ninenines">>, <<"eu">>], [
{[<<"users">>, '_', <<"mails">>], match_any_subdomain_users, []}
]},
- {[<<"dev-extend">>, <<"eu">>], [
+ {[<<"ninenines">>, <<"eu">>], [
{[<<"users">>, id, <<"friends">>], match_extend_users_friends, []},
{'_', match_extend, []}
]},
- {[<<"dev-extend">>, var], [
+ {[<<"ninenines">>, var], [
{[<<"threads">>, var], match_duplicate_vars,
[we, {expect, two}, var, here]}
]},
@@ -255,22 +255,22 @@ match_test_() ->
%% {Host, Path, Result}
Tests = [
{[<<"any">>], [], {ok, match_any, [], []}},
- {[<<"www">>, <<"any">>, <<"dev-extend">>, <<"eu">>],
+ {[<<"www">>, <<"any">>, <<"ninenines">>, <<"eu">>],
[<<"users">>, <<"42">>, <<"mails">>],
{ok, match_any_subdomain_users, [], []}},
- {[<<"www">>, <<"dev-extend">>, <<"eu">>],
+ {[<<"www">>, <<"ninenines">>, <<"eu">>],
[<<"users">>, <<"42">>, <<"mails">>], {ok, match_any, [], []}},
- {[<<"www">>, <<"dev-extend">>, <<"eu">>], [], {ok, match_any, [], []}},
- {[<<"www">>, <<"any">>, <<"dev-extend">>, <<"eu">>],
+ {[<<"www">>, <<"ninenines">>, <<"eu">>], [], {ok, match_any, [], []}},
+ {[<<"www">>, <<"any">>, <<"ninenines">>, <<"eu">>],
[<<"not_users">>, <<"42">>, <<"mails">>], {error, notfound, path}},
- {[<<"dev-extend">>, <<"eu">>], [], {ok, match_extend, [], []}},
- {[<<"dev-extend">>, <<"eu">>], [<<"users">>, <<"42">>, <<"friends">>],
+ {[<<"ninenines">>, <<"eu">>], [], {ok, match_extend, [], []}},
+ {[<<"ninenines">>, <<"eu">>], [<<"users">>, <<"42">>, <<"friends">>],
{ok, match_extend_users_friends, [], [{id, <<"42">>}]}},
{[<<"erlang">>, <<"fr">>], '_',
{ok, match_erlang_ext, [], [{ext, <<"fr">>}]}},
{[<<"any">>], [<<"users">>, <<"444">>, <<"friends">>],
{ok, match_users_friends, [], [{id, <<"444">>}]}},
- {[<<"dev-extend">>, <<"fr">>], [<<"threads">>, <<"987">>],
+ {[<<"ninenines">>, <<"fr">>], [<<"threads">>, <<"987">>],
{ok, match_duplicate_vars, [we, {expect, two}, var, here],
[{var, <<"fr">>}, {var, <<"987">>}]}}
],
@@ -280,27 +280,27 @@ match_test_() ->
match_info_test_() ->
Dispatch = [
- {[<<"www">>, <<"dev-extend">>, <<"eu">>], [
+ {[<<"www">>, <<"ninenines">>, <<"eu">>], [
{[<<"pathinfo">>, <<"is">>, <<"next">>, '...'], match_path, []}
]},
- {['...', <<"dev-extend">>, <<"eu">>], [
+ {['...', <<"ninenines">>, <<"eu">>], [
{'_', match_any, []}
]}
],
Tests = [
- {[<<"dev-extend">>, <<"eu">>], [],
+ {[<<"ninenines">>, <<"eu">>], [],
{ok, match_any, [], [], [], undefined}},
- {[<<"bugs">>, <<"dev-extend">>, <<"eu">>], [],
+ {[<<"bugs">>, <<"ninenines">>, <<"eu">>], [],
{ok, match_any, [], [], [<<"bugs">>], undefined}},
- {[<<"cowboy">>, <<"bugs">>, <<"dev-extend">>, <<"eu">>], [],
+ {[<<"cowboy">>, <<"bugs">>, <<"ninenines">>, <<"eu">>], [],
{ok, match_any, [], [], [<<"cowboy">>, <<"bugs">>], undefined}},
- {[<<"www">>, <<"dev-extend">>, <<"eu">>],
+ {[<<"www">>, <<"ninenines">>, <<"eu">>],
[<<"pathinfo">>, <<"is">>, <<"next">>],
{ok, match_path, [], [], undefined, []}},
- {[<<"www">>, <<"dev-extend">>, <<"eu">>],
+ {[<<"www">>, <<"ninenines">>, <<"eu">>],
[<<"pathinfo">>, <<"is">>, <<"next">>, <<"path_info">>],
{ok, match_path, [], [], undefined, [<<"path_info">>]}},
- {[<<"www">>, <<"dev-extend">>, <<"eu">>],
+ {[<<"www">>, <<"ninenines">>, <<"eu">>],
[<<"pathinfo">>, <<"is">>, <<"next">>, <<"foo">>, <<"bar">>],
{ok, match_path, [], [], undefined, [<<"foo">>, <<"bar">>]}}
],
View
18 src/cowboy_http.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
@@ -270,7 +270,15 @@ maybe_qparam(Data, Fun) ->
fun (<< $;, Rest/binary >>) ->
whitespace(Rest,
fun (Rest2) ->
- qparam(Rest2, Fun)
+ %% This is a non-strict parsing clause required by some user agents
+ %% that use the wrong delimiter putting a charset where a qparam is
+ %% expected.
+ try qparam(Rest2, Fun) of
+ Result -> Result
+ catch
+ error:function_clause ->
+ Fun(<<",", Rest2/binary>>, 1000)
+ end
end);
(Rest) ->
Fun(Rest, 1000)
@@ -885,6 +893,12 @@ nonempty_charset_list_test_() ->
{<<"iso-8859-5, unicode-1-1;q=0.8">>, [
{<<"iso-8859-5">>, 1000},
{<<"unicode-1-1">>, 800}
+ ]},
+ %% Some user agents send this invalid value for the Accept-Charset header
+ {<<"ISO-8859-1;utf-8;q=0.7,*;q=0.7">>, [
+ {<<"iso-8859-1">>, 1000},
+ {<<"utf-8">>, 700},
+ {<<"*">>, 700}
]}
],
[{V, fun() -> R = nonempty_list(V, fun conneg/2) end} || {V, R} <- Tests].
View
2 src/cowboy_http_handler.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
7 src/cowboy_http_protocol.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
@@ -438,12 +438,13 @@ ensure_response(#http_req{socket=Socket, transport=Transport,
%% 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}) ->
+error_terminate(Code, State=#state{socket=Socket, transport=Transport,
+ onresponse=OnResponse}) ->
receive
{cowboy_http_req, resp_sent} -> ok
after 0 ->
_ = cowboy_http_req:reply(Code, #http_req{
- socket=Socket, transport=Transport,
+ socket=Socket, transport=Transport, onresponse=OnResponse,
connection=close, pid=self(), resp_state=waiting}),
ok
end,
View
32 src/cowboy_http_req.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
@@ -576,21 +576,13 @@ multipart_data(Req=#http_req{body_state=waiting}) ->
{{<<"multipart">>, _SubType, Params}, Req2} =
parse_header('Content-Type', Req),
{_, Boundary} = lists:keyfind(<<"boundary">>, 1, Params),
- {Length, Req3=#http_req{buffer=Buffer}} =
- parse_header('Content-Length', Req2),
- multipart_data(Req3, Length, cowboy_multipart:parser(Boundary), Buffer);
+ {Length, Req3} = parse_header('Content-Length', Req2),
+ multipart_data(Req3, Length, {more, cowboy_multipart:parser(Boundary)});
multipart_data(Req=#http_req{body_state={multipart, Length, Cont}}) ->
multipart_data(Req, Length, Cont());
multipart_data(Req=#http_req{body_state=done}) ->
{eof, Req}.
-multipart_data(Req, Length, Parser, Buffer) when byte_size(Buffer) >= Length ->
- << Data:Length/binary, Rest/binary >> = Buffer,
- multipart_data(Req#http_req{buffer=Rest}, 0, Parser(Data));
-multipart_data(Req, Length, Parser, Buffer) ->
- NewLength = Length - byte_size(Buffer),
- multipart_data(Req#http_req{buffer= <<>>}, NewLength, Parser(Buffer)).
-
multipart_data(Req, Length, {headers, Headers, Cont}) ->
{{headers, Headers}, Req#http_req{body_state={multipart, Length, Cont}}};
multipart_data(Req, Length, {body, Data, Cont}) ->
@@ -601,15 +593,15 @@ multipart_data(Req, 0, eof) ->
{eof, Req#http_req{body_state=done}};
multipart_data(Req=#http_req{socket=Socket, transport=Transport},
Length, eof) ->
+ %% We just want to skip so no need to stream data here.
{ok, _Data} = Transport:recv(Socket, Length, 5000),
{eof, Req#http_req{body_state=done}};
-multipart_data(Req=#http_req{socket=Socket, transport=Transport},
- Length, {more, Parser}) when Length > 0 ->
- case Transport:recv(Socket, 0, 5000) of
- {ok, << Data:Length/binary, Buffer/binary >>} ->
- multipart_data(Req#http_req{buffer=Buffer}, 0, Parser(Data));
- {ok, Data} ->
- multipart_data(Req, Length - byte_size(Data), Parser(Data))
+multipart_data(Req, Length, {more, Parser}) when Length > 0 ->
+ case stream_body(Req) of
+ {ok, << Data:Length/binary, Buffer/binary >>, Req2} ->
+ multipart_data(Req2#http_req{buffer=Buffer}, 0, Parser(Data));
+ {ok, Data, Req2} ->
+ multipart_data(Req2, Length - byte_size(Data), Parser(Data))
end.
%% @doc Skip a part returned by the multipart parser.
@@ -918,6 +910,9 @@ status(423) -> <<"423 Locked">>;
status(424) -> <<"424 Failed Dependency">>;
status(425) -> <<"425 Unordered Collection">>;
status(426) -> <<"426 Upgrade Required">>;
+status(428) -> <<"428 Precondition Required">>;
+status(429) -> <<"429 Too Many Requests">>;
+status(431) -> <<"431 Request Header Fields Too Large">>;
status(500) -> <<"500 Internal Server Error">>;
status(501) -> <<"501 Not Implemented">>;
status(502) -> <<"502 Bad Gateway">>;
@@ -927,6 +922,7 @@ status(505) -> <<"505 HTTP Version Not Supported">>;
status(506) -> <<"506 Variant Also Negotiates">>;
status(507) -> <<"507 Insufficient Storage">>;
status(510) -> <<"510 Not Extended">>;
+status(511) -> <<"511 Network Authentication Required">>;
status(B) when is_binary(B) -> B.
-spec header_to_binary(cowboy_http:header()) -> binary().
View
2 src/cowboy_http_rest.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
58 src/cowboy_http_static.erl
@@ -31,7 +31,7 @@
%% The handler must be configured with a request path prefix to serve files
%% under and the path to a directory to read files from. The request path prefix
%% is defined in the path pattern of the cowboy dispatch rule for the handler.
-%% The request path pattern must end with a ``'...''' token.
+%% The request path pattern must end with a `...' token.
%% The directory path can be set to either an absolute or relative path in the
%% form of a list or binary string representation of a file system path. A list
%% of binary path segments, as is used throughout cowboy, is also a valid
@@ -133,6 +133,40 @@
%% [Checksum|_] = string:tokens(os:cmd(ChecksumCommand), " "),
%% {strong, iolist_to_binary(Checksum)}.
%% '''
+%%
+%% == File configuration ==
+%%
+%% If the file system path being served does not share a common suffix with
+%% the request path it is possible to override the file path using the `file'
+%% option. The value of this option is expected to be a relative path within
+%% the static file directory specified using the `directory' option.
+%% The path must be in the form of a list or binary string representation of a
+%% file system path. A list of binary path segments, as is used throughout
+%% cowboy, is also a valid.
+%%
+%% When the `file' option is used the same file will be served for all requests
+%% matching the cowboy dispatch fule for the handler. It is not necessary to
+%% end the request path pattern with a `...' token because the request path
+%% will not be used to determine which file to serve from the static directory.
+%%
+%% === Examples ===
+%%
+%% ```
+%% %% Serve cowboy/priv/www/index.html as http://example.com/
+%% {[], cowboy_http_static,
+%% [{directory, {priv_dir, cowboy, [<<"www">>]}}
+%% {file, <<"index.html">>}]}
+%%
+%% %% Serve cowboy/priv/www/page.html under http://example.com/*/page
+%% {['*', <<"page">>], cowboy_http_static,
+%% [{directory, {priv_dir, cowboy, [<<"www">>]}}
+%% {file, <<"page.html">>}]}.
+%%
+%% %% Always serve cowboy/priv/www/other.html under http://example.com/other
+%% {[<<"other">>, '...'], cowboy_http_static,
+%% [{directory, {priv_dir, cowboy, [<<"www">>]}}
+%% {file, "other.html"}]}
+%% '''
-module(cowboy_http_static).
%% include files
@@ -189,7 +223,10 @@ rest_init(Req, Opts) ->
{attributes, Attrs} -> {fun attr_etag_function/2, Attrs};
{_, _}=ETagFunction1 -> ETagFunction1
end,
- {Filepath, Req1} = cowboy_http_req:path_info(Req),
+ {Filepath, Req1} = case lists:keyfind(file, 1, Opts) of
+ {_, Filepath2} -> {filepath_path(Filepath2), Req};
+ false -> cowboy_http_req:path_info(Req)
+ end,
State = case check_path(Filepath) of
error ->
#state{filepath=error, fileinfo=error, mimetypes=undefined,
@@ -341,6 +378,14 @@ directory_path({priv_dir, App, Path}) when is_binary(Path) ->
directory_path(Path) ->
Path.
+%% @private Ensure that a file path is of the same type as a request path.
+-spec filepath_path(dirpath()) -> Path::[binary()].
+filepath_path([H|_]=Path) when is_integer(H) ->
+ filename:split(list_to_binary(Path));
+filepath_path(Path) when is_binary(Path) ->
+ filename:split(Path);
+filepath_path([H|_]=Path) when is_binary(H) ->
+ Path.
%% @private Validate a request path for unsafe characters.
%% There is no way to escape special characters in a filesystem path.
@@ -459,5 +504,14 @@ directory_path_test_() ->
?_eq("a/b", P("a/b"))
].
+filepath_path_test_() ->
+ P = fun filepath_path/1,
+ [?_eq([<<"a">>], P("a")),
+ ?_eq([<<"a">>], P(<<"a">>)),
+ ?_eq([<<"a">>], P([<<"a">>])),
+ ?_eq([<<"a">>, <<"b">>], P("a/b")),
+ ?_eq([<<"a">>, <<"b">>], P(<<"a/b">>)),
+ ?_eq([<<"a">>, <<"b">>], P([<<"a">>, <<"b">>]))
+ ].
-endif.
View
2 src/cowboy_http_websocket.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 src/cowboy_http_websocket_handler.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 src/cowboy_listener.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 src/cowboy_listener_sup.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 src/cowboy_protocol.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%% Copyright (c) 2011, Michiel Hakvoort <michiel@hakvoort.it>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
View
2 src/cowboy_requests_sup.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 src/cowboy_ssl_transport.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 src/cowboy_sup.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 src/cowboy_tcp_transport.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 test/dispatcher_prop.erl
@@ -1,5 +1,5 @@
%% Copyright (c) 2011, Magnus Klaar <magnus.klaar@gmail.com>
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
43 test/http_SUITE.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
@@ -45,6 +45,7 @@
-export([nc_zero/1]).
-export([onrequest/1]).
-export([onrequest_reply/1]).
+-export([onresponse_crash/1]).
-export([onresponse_reply/1]).
-export([pipeline/1]).
-export([rest_keepalive/1]).
@@ -58,6 +59,8 @@
-export([static_attribute_etag/1]).
-export([static_function_etag/1]).
-export([static_mimetypes_function/1]).
+-export([static_specify_file/1]).
+-export([static_specify_file_catchall/1]).
-export([static_test_file/1]).
-export([static_test_file_css/1]).
-export([stream_body_set_resp/1]).
@@ -101,6 +104,8 @@ groups() ->
static_attribute_etag,
static_function_etag,
static_mimetypes_function,
+ static_specify_file,
+ static_specify_file_catchall,
static_test_file,
static_test_file_css,
stream_body_set_resp,
@@ -116,6 +121,7 @@ groups() ->
onrequest_reply
]},
{onresponse, [], [
+ onresponse_crash,
onresponse_reply
]}
].
@@ -134,7 +140,7 @@ init_per_group(http, Config) ->
Port = 33080,
Transport = cowboy_tcp_transport,
Config1 = init_static_dir(Config),
- cowboy:start_listener(http, 100,
+ {ok, _} = cowboy:start_listener(http, 100,
Transport, [{port, Port}],
cowboy_http_protocol, [
{dispatch, init_dispatch(Config1)},
@@ -156,7 +162,7 @@ init_per_group(https, Config) ->
application:start(crypto),
application:start(public_key),
application:start(ssl),
- {ok,_} = cowboy:start_listener(https, 100,
+ {ok, _} = cowboy:start_listener(https, 100,
Transport, Opts ++ [{port, Port}],
cowboy_http_protocol, [
{dispatch, init_dispatch(Config1)},
@@ -241,6 +247,10 @@ init_dispatch(Config) ->
{[<<"static_function_etag">>, '...'], cowboy_http_static,
[{directory, ?config(static_dir, Config)},
{etag, {fun static_function_etag/2, etag_data}}]},
+ {[<<"static_specify_file">>, '...'], cowboy_http_static,
+ [{directory, ?config(static_dir, Config)},
+ {mimetypes, [{<<".css">>, [<<"text/css">>]}]},
+ {file, <<"test_file.css">>}]},
{[<<"multipart">>], http_handler_multipart, []},
{[<<"echo">>, <<"body">>], http_handler_echo_body, []},
{[<<"simple">>], rest_simple_resource, []},
@@ -348,7 +358,7 @@ The document has moved
{400, "\n"},
{400, "Garbage\r\n\r\n"},
{400, "\r\n\r\n\r\n\r\n\r\n\r\n"},
- {400, "GET / HTTP/1.1\r\nHost: dev-extend.eu\r\n\r\n"},
+ {400, "GET / HTTP/1.1\r\nHost: ninenines.eu\r\n\r\n"},
{400, "GET http://proxy/ HTTP/1.1\r\n\r\n"},
{400, ResponsePacket},
{408, "GET / HTTP/1.1\r\n"},
@@ -604,6 +614,13 @@ onrequest_hook(Req) ->
Req3
end.
+onresponse_crash(Config) ->
+ Client = ?config(client, Config),
+ {ok, Client2} = cowboy_client:request(<<"GET">>,
+ build_url("/handler_errors?case=init_before_reply", Config), Client),
+ {ok, 777, Headers, _} = cowboy_client:response(Client2),
+ {<<"x-hook">>, <<"onresponse">>} = lists:keyfind(<<"x-hook">>, 1, Headers).
+
onresponse_reply(Config) ->
Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>,
@@ -796,6 +813,24 @@ static_mimetypes_function(Config) ->
{<<"content-type">>, <<"text/html">>}
= lists:keyfind(<<"content-type">>, 1, Headers).
+static_specify_file(Config) ->
+ Client = ?config(client, Config),
+ {ok, Client2} = cowboy_client:request(<<"GET">>,
+ build_url("/static_specify_file", Config), Client),
+ {ok, 200, Headers, Client3} = cowboy_client:response(Client2),
+ {<<"content-type">>, <<"text/css">>}
+ = lists:keyfind(<<"content-type">>, 1, Headers),
+ {ok, <<"test_file.css\n">>, _} = cowboy_client:response_body(Client3).
+
+static_specify_file_catchall(Config) ->
+ Client = ?config(client, Config),
+ {ok, Client2} = cowboy_client:request(<<"GET">>,
+ build_url("/static_specify_file/none", Config), Client),
+ {ok, 200, Headers, Client3} = cowboy_client:response(Client2),
+ {<<"content-type">>, <<"text/css">>}
+ = lists:keyfind(<<"content-type">>, 1, Headers),
+ {ok, <<"test_file.css\n">>, _} = cowboy_client:response_body(Client3).
+
static_test_file(Config) ->
Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>,
View
2 test/proper_SUITE.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
View
2 test/ws_SUITE.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
+%% Copyright (c) 2011, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above

No commit comments for this range

Something went wrong with that request. Please try again.