0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-16 13:10:24 +00:00

Add cowboy_bstr:capitalize_token/1

For optional header name capitalization. See the guide section about it.
This commit is contained in:
Loïc Hoguin 2013-01-17 23:37:50 +01:00
parent cd680706cd
commit 1b996794ee
3 changed files with 71 additions and 3 deletions

View file

@ -6,6 +6,30 @@ Architecture
@todo Describe.
Lowercase header names
----------------------
For consistency reasons it has been chosen to convert all header names
to lowercase binary strings. This prevents the programmer from making
case mistakes, and is possible because header names are case insensitive.
This works fine for the large majority of clients. However, some badly
implemented clients, especially ones found in corporate code or closed
source products, may not handle header names in a case insensitive manner.
This means that when Cowboy returns lowercase header names, these clients
will not find the headers they are looking for.
A simple way to solve this is to create an `onresponse` hook that will
format the header names with the expected case.
``` erlang
capitalize_hook(Status, Headers, Body, Req) ->
Headers2 = [{cowboy_bstr:capitalize_token(N), V}
|| {N, V} <- Headers],
{ok, Req2} = cowboy_req:reply(State, Headers2, Body, Req),
Req2.
```
Efficiency considerations
-------------------------

View file

@ -51,4 +51,5 @@ Cowboy User Guide
* Handler middleware
* [Internals](internals.md)
* Architecture
* Lowercase header names
* Efficiency considerations

View file

@ -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.