~ubuntu-branches/ubuntu/saucy/rabbitmq-server/saucy

« back to all changes in this revision

Viewing changes to plugins-src/mochiweb-wrapper/mochiweb-git/src/mochiweb_request.erl

  • Committer: Package Import Robot
  • Author(s): Emile Joubert
  • Date: 2012-09-27 16:28:21 UTC
  • mfrom: (0.2.17) (0.1.31 sid)
  • Revision ID: package-import@ubuntu.com-20120927162821-yzw9w4i6uu4tczu5
Tags: 2.8.7-1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
-export([parse_cookie/0, get_cookie_value/1]).
22
22
-export([serve_file/2, serve_file/3]).
23
23
-export([accepted_encodings/1]).
 
24
-export([accepts_content_type/1, accepted_content_types/1]).
24
25
 
25
26
-define(SAVE_QS, mochiweb_request_qs).
26
27
-define(SAVE_PATH, mochiweb_request_path).
31
32
-define(SAVE_COOKIE, mochiweb_request_cookie).
32
33
-define(SAVE_FORCE_CLOSE, mochiweb_request_force_close).
33
34
 
34
 
%% @type iolist() = [iolist() | binary() | char()].
35
 
%% @type iodata() = binary() | iolist().
36
35
%% @type key() = atom() | string() | binary()
37
36
%% @type value() = atom() | string() | binary() | integer()
38
37
%% @type headers(). A mochiweb_headers structure.
39
38
%% @type response(). A mochiweb_response parameterized module instance.
40
39
%% @type ioheaders() = headers() | [{key(), value()}].
41
40
 
42
 
% 10 second default idle timeout
43
 
-define(IDLE_TIMEOUT, 10000).
 
41
% 5 minute default idle timeout
 
42
-define(IDLE_TIMEOUT, 300000).
44
43
 
45
44
% Maximum recv_body() length of 1MB
46
45
-define(MAX_RECV_BODY, 104857600).
222
221
             end,
223
222
    case Expect of
224
223
        "100-continue" ->
225
 
            start_raw_response({100, gb_trees:empty()});
 
224
            _ = start_raw_response({100, gb_trees:empty()}),
 
225
            ok;
226
226
        _Else ->
227
227
            ok
228
228
    end,
244
244
                exit({body_too_large, content_length});
245
245
            _ ->
246
246
                stream_unchunked_body(Length, ChunkFun, FunState)
247
 
            end;
248
 
        Length ->
249
 
            exit({length_not_integer, Length})
 
247
            end
250
248
    end.
251
249
 
252
250
 
381
379
%% @doc Return true if the connection must be closed. If false, using
382
380
%%      Keep-Alive should be safe.
383
381
should_close() ->
384
 
    ForceClose = erlang:get(mochiweb_request_force_close) =/= undefined,
385
 
    DidNotRecv = erlang:get(mochiweb_request_recv) =:= undefined,
 
382
    ForceClose = erlang:get(?SAVE_FORCE_CLOSE) =/= undefined,
 
383
    DidNotRecv = erlang:get(?SAVE_RECV) =:= undefined,
386
384
    ForceClose orelse Version < {1, 0}
387
385
        %% Connection: close
388
386
        orelse get_header_value("connection") =:= "close"
400
398
%% @doc Clean up any junk in the process dictionary, required before continuing
401
399
%%      a Keep-Alive request.
402
400
cleanup() ->
403
 
    [erase(K) || K <- [?SAVE_QS,
404
 
                       ?SAVE_PATH,
405
 
                       ?SAVE_RECV,
406
 
                       ?SAVE_BODY,
407
 
                       ?SAVE_POST,
408
 
                       ?SAVE_COOKIE,
409
 
                       ?SAVE_FORCE_CLOSE]],
 
401
    L = [?SAVE_QS, ?SAVE_PATH, ?SAVE_RECV, ?SAVE_BODY, ?SAVE_BODY_LENGTH,
 
402
         ?SAVE_POST, ?SAVE_COOKIE, ?SAVE_FORCE_CLOSE],
 
403
    lists:foreach(fun(K) ->
 
404
                          erase(K)
 
405
                  end, L),
410
406
    ok.
411
407
 
412
408
%% @spec parse_qs() -> [{Key::string(), Value::string()}]
498
494
%% @spec read_chunk_length() -> integer()
499
495
%% @doc Read the length of the next HTTP chunk.
500
496
read_chunk_length() ->
501
 
    mochiweb_socket:setopts(Socket, [{packet, line}]),
 
497
    ok = mochiweb_socket:setopts(Socket, [{packet, line}]),
502
498
    case mochiweb_socket:recv(Socket, 0, ?IDLE_TIMEOUT) of
503
499
        {ok, Header} ->
504
 
            mochiweb_socket:setopts(Socket, [{packet, raw}]),
 
500
            ok = mochiweb_socket:setopts(Socket, [{packet, raw}]),
505
501
            Splitter = fun (C) ->
506
502
                               C =/= $\r andalso C =/= $\n andalso C =/= $
507
503
                       end,
515
511
%% @doc Read in a HTTP chunk of the given length. If Length is 0, then read the
516
512
%%      HTTP footers (as a list of binaries, since they're nominal).
517
513
read_chunk(0) ->
518
 
    mochiweb_socket:setopts(Socket, [{packet, line}]),
 
514
    ok = mochiweb_socket:setopts(Socket, [{packet, line}]),
519
515
    F = fun (F1, Acc) ->
520
516
                case mochiweb_socket:recv(Socket, 0, ?IDLE_TIMEOUT) of
521
517
                    {ok, <<"\r\n">>} ->
527
523
                end
528
524
        end,
529
525
    Footers = F(F, []),
530
 
    mochiweb_socket:setopts(Socket, [{packet, raw}]),
 
526
    ok = mochiweb_socket:setopts(Socket, [{packet, raw}]),
531
527
    put(?SAVE_RECV, true),
532
528
    Footers;
533
529
read_chunk(Length) ->
612
608
                                      [{"last-modified", LastModified}
613
609
                                       | ExtraHeaders],
614
610
                                      {file, IoDevice}}),
615
 
                            file:close(IoDevice),
 
611
                            ok = file:close(IoDevice),
616
612
                            Res;
617
613
                        _ ->
618
614
                            not_found(ExtraHeaders)
707
703
            )
708
704
    end.
709
705
 
 
706
%% @spec accepts_content_type(string() | binary()) -> boolean() | bad_accept_header
 
707
%%
 
708
%% @doc Determines whether a request accepts a given media type by analyzing its
 
709
%%      "Accept" header.
 
710
%%
 
711
%%      Examples
 
712
%%
 
713
%%      1) For a missing "Accept" header:
 
714
%%         accepts_content_type("application/json") -> true
 
715
%%
 
716
%%      2) For an "Accept" header with value "text/plain, application/*":
 
717
%%         accepts_content_type("application/json") -> true
 
718
%%
 
719
%%      3) For an "Accept" header with value "text/plain, */*; q=0.0":
 
720
%%         accepts_content_type("application/json") -> false
 
721
%%
 
722
%%      4) For an "Accept" header with value "text/plain; q=0.5, */*; q=0.1":
 
723
%%         accepts_content_type("application/json") -> true
 
724
%%
 
725
%%      5) For an "Accept" header with value "text/*; q=0.0, */*":
 
726
%%         accepts_content_type("text/plain") -> false
 
727
%%
 
728
accepts_content_type(ContentType1) ->
 
729
    ContentType = re:replace(ContentType1, "\\s", "", [global, {return, list}]),
 
730
    AcceptHeader = accept_header(),
 
731
    case mochiweb_util:parse_qvalues(AcceptHeader) of
 
732
        invalid_qvalue_string ->
 
733
            bad_accept_header;
 
734
        QList ->
 
735
            [MainType, _SubType] = string:tokens(ContentType, "/"),
 
736
            SuperType = MainType ++ "/*",
 
737
            lists:any(
 
738
                fun({"*/*", Q}) when Q > 0.0 ->
 
739
                        true;
 
740
                    ({Type, Q}) when Q > 0.0 ->
 
741
                        Type =:= ContentType orelse Type =:= SuperType;
 
742
                    (_) ->
 
743
                        false
 
744
                end,
 
745
                QList
 
746
            ) andalso
 
747
            (not lists:member({ContentType, 0.0}, QList)) andalso
 
748
            (not lists:member({SuperType, 0.0}, QList))
 
749
    end.
 
750
 
 
751
%% @spec accepted_content_types([string() | binary()]) -> [string()] | bad_accept_header
 
752
%%
 
753
%% @doc Filters which of the given media types this request accepts. This filtering
 
754
%%      is performed by analyzing the "Accept" header. The returned list is sorted
 
755
%%      according to the preferences specified in the "Accept" header (higher Q values
 
756
%%      first). If two or more types have the same preference (Q value), they're order
 
757
%%      in the returned list is the same as they're order in the input list.
 
758
%%
 
759
%%      Examples
 
760
%%
 
761
%%      1) For a missing "Accept" header:
 
762
%%         accepted_content_types(["text/html", "application/json"]) ->
 
763
%%             ["text/html", "application/json"]
 
764
%%
 
765
%%      2) For an "Accept" header with value "text/html, application/*":
 
766
%%         accepted_content_types(["application/json", "text/html"]) ->
 
767
%%             ["application/json", "text/html"]
 
768
%%
 
769
%%      3) For an "Accept" header with value "text/html, */*; q=0.0":
 
770
%%         accepted_content_types(["text/html", "application/json"]) ->
 
771
%%             ["text/html"]
 
772
%%
 
773
%%      4) For an "Accept" header with value "text/html; q=0.5, */*; q=0.1":
 
774
%%         accepts_content_types(["application/json", "text/html"]) ->
 
775
%%             ["text/html", "application/json"]
 
776
%%
 
777
accepted_content_types(Types1) ->
 
778
    Types = lists:map(
 
779
        fun(T) -> re:replace(T, "\\s", "", [global, {return, list}]) end,
 
780
        Types1),
 
781
    AcceptHeader = accept_header(),
 
782
    case mochiweb_util:parse_qvalues(AcceptHeader) of
 
783
        invalid_qvalue_string ->
 
784
            bad_accept_header;
 
785
        QList ->
 
786
            TypesQ = lists:foldr(
 
787
                fun(T, Acc) ->
 
788
                    case proplists:get_value(T, QList) of
 
789
                        undefined ->
 
790
                            [MainType, _SubType] = string:tokens(T, "/"),
 
791
                            case proplists:get_value(MainType ++ "/*", QList) of
 
792
                                undefined ->
 
793
                                    case proplists:get_value("*/*", QList) of
 
794
                                        Q when is_float(Q), Q > 0.0 ->
 
795
                                            [{Q, T} | Acc];
 
796
                                        _ ->
 
797
                                            Acc
 
798
                                    end;
 
799
                                Q when Q > 0.0 ->
 
800
                                    [{Q, T} | Acc];
 
801
                                _ ->
 
802
                                    Acc
 
803
                            end;
 
804
                        Q when Q > 0.0 ->
 
805
                            [{Q, T} | Acc];
 
806
                        _ ->
 
807
                            Acc
 
808
                    end
 
809
                end,
 
810
                [], Types),
 
811
            % Note: Stable sort. If 2 types have the same Q value we leave them in the
 
812
            % same order as in the input list.
 
813
            SortFun = fun({Q1, _}, {Q2, _}) -> Q1 >= Q2 end,
 
814
            [Type || {_Q, Type} <- lists:sort(SortFun, TypesQ)]
 
815
    end.
 
816
 
 
817
accept_header() ->
 
818
    case get_header_value("Accept") of
 
819
        undefined ->
 
820
            "*/*";
 
821
        Value ->
 
822
            Value
 
823
    end.
 
824
 
710
825
%%
711
826
%% Tests
712
827
%%