355
367
%% {stop, Reason, State} (terminate/2 is called)
356
368
%% Description: Handling call messages
357
369
%%--------------------------------------------------------------------
358
handle_call({request, Request}, _From, State) ->
359
?hcrv("request", [{request, Request}]),
370
handle_call({request, Request}, _, State) ->
371
?hcri("request", [{request, Request}]),
360
372
case (catch handle_request(Request, State)) of
361
{ok, ReqId, NewState} ->
362
{reply, {ok, ReqId}, NewState};
373
{reply, Msg, NewState} ->
374
{reply, Msg, NewState};
365
NewError = {error, {failed_process_request, Error}},
366
{reply, NewError, State}
376
{stop, Error, httpc_response:error(Request, Error), State}
369
handle_call({cancel_request, RequestId}, From,
370
#state{handler_db = HandlerDb} = State) ->
371
?hcrv("cancel_request", [{request_id, RequestId}]),
379
handle_call({cancel_request, RequestId}, From, State) ->
380
?hcri("cancel_request", [{request_id, RequestId}]),
372
381
case ets:lookup(State#state.handler_db, RequestId) of
374
?hcrd("nothing to cancel", []),
375
Reply = ok, %% Nothing to cancel
376
{reply, Reply, State};
378
[#handler_info{handler = Pid}] when is_pid(Pid) ->
379
?hcrd("found operational handler for this request",
381
httpc_handler:cancel(RequestId, Pid),
382
{noreply, State#state{cancel =
383
[{RequestId, Pid, From} |
384
State#state.cancel]}};
386
[#handler_info{starter = Pid, state = HandlerState}]
388
?hcri("found *initiating* handler for this request",
389
[{starter, Pid}, {state, HandlerState}]),
390
ets:update_element(HandlerDb, RequestId,
391
{#handler_info.state, canceled}),
392
{noreply, State#state{cancel =
393
[{RequestId, Pid, From} |
383
%% The request has allready compleated make sure
384
%% it is deliverd to the client process queue so
385
%% it can be thrown away by httpc:cancel_request
386
%% This delay is hopfully a temporary workaround.
387
%% Note that it will not not delay the manager,
388
%% only the client that called httpc:cancel_request
389
timer:apply_after(?DELAY, gen_server, reply, [From, ok]),
392
httpc_handler:cancel(RequestId, Pid, From),
393
{noreply, State#state{cancel =
394
[{RequestId, Pid, From} |
394
395
State#state.cancel]}}
398
398
handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) ->
548
519
%% {stop, Reason, State} (terminate/2 is called)
549
520
%% Description: Handling all non call/cast messages
550
521
%%---------------------------------------------------------
552
handle_info({started, StarterPid, ReqId, HandlerPid}, State) ->
553
handle_started(StarterPid, ReqId, HandlerPid, State),
556
handle_info({connect_and_send, StarterPid, ReqId, HandlerPid, Res}, State) ->
557
handle_connect_and_send(StarterPid, ReqId, HandlerPid, Res, State),
560
handle_info({failed_starting_handler, StarterPid, ReqId, Res}, State) ->
561
handle_failed_starting_handler(StarterPid, ReqId, Res, State),
564
handle_info({'EXIT', Pid, Reason}, #state{handler_db = HandlerDb} = State) ->
565
maybe_handle_terminating_starter(Pid, Reason, HandlerDb),
522
handle_info({'EXIT', _, _}, State) ->
568
525
handle_info({'DOWN', _, _, Pid, _}, State) ->
571
%% Normally this should have been cleaned up already
572
%% (when receiving {request_done, PequestId}), but
573
%% just in case there is a glitch, cleanup anyway.
576
Pattern = #handler_info{handler = Pid, _ = '_'},
577
ets:match_delete(State#state.handler_db, Pattern),
526
ets:match_delete(State#state.handler_db, {'_', Pid, '_'}),
579
528
%% If there where any canceled request, handled by the
580
529
%% the process that now has terminated, the
581
530
%% cancelation can be viewed as sucessfull!
583
lists:foldl(fun({_, HandlerPid, From} = Entry, Acc) ->
532
lists:foldl(fun(Entry = {_, HandlerPid, From}, Acc) ->
584
533
case HandlerPid of
586
535
gen_server:reply(From, ok),
655
602
{Pid, State} <- Handlers2],
660
%% The request handler process is started asynchronously by a
661
%% "starter process". When the handler has sucessfully been started,
662
%% this message (started) is sent.
665
handle_started(StarterPid, ReqId, HandlerPid,
666
#state{profile_name = Profile,
667
handler_db = HandlerDb}) ->
668
case ets:lookup(HandlerDb, ReqId) of
669
[#handler_info{state = initiating} = HandlerInfo] ->
670
?hcri("received started ack for initiating handler", []),
671
%% As a last resort, make sure we know when it exits,
672
%% in case it forgets to notify us.
673
%% We dont need to know the ref id?
674
erlang:monitor(process, HandlerPid),
675
HandlerInfo2 = HandlerInfo#handler_info{handler = HandlerPid,
677
ets:insert(HandlerDb, HandlerInfo2),
680
[#handler_info{state = State}] ->
681
error_report(Profile,
682
"unexpected (started) message for handler (~p) in state "
683
"~p regarding request ~p - ignoring", [HandlerPid, State, ReqId]),
684
?hcri("received unexpected started message", [{state, State}]),
688
error_report(Profile,
689
"unknown handler ~p (~p) started for request ~w - canceling",
690
[HandlerPid, StarterPid, ReqId]),
691
httpc_handler:cancel(ReqId, HandlerPid)
696
%% The request handler process is started asynchronously by a
697
%% "starter process". When that process terminates it sends
698
%% one of two messages. These ara handled by the two functions
702
handle_connect_and_send(_StarterPid, ReqId, HandlerPid, Result,
703
#state{profile_name = Profile,
704
handler_db = HandlerDb}) ->
705
case ets:lookup(HandlerDb, ReqId) of
706
[#handler_info{state = started} = HandlerInfo] when Result =:= ok ->
707
?hcri("received connect-and-send ack for started handler", []),
708
HandlerInfo2 = HandlerInfo#handler_info{starter = undefined,
709
handler = HandlerPid,
710
state = operational},
711
ets:insert(HandlerDb, HandlerInfo2),
714
[#handler_info{state = canceled} = HandlerInfo] when Result =:= ok ->
715
?hcri("received connect-and-send ack for canceled handler", []),
716
httpc_handler:cancel(ReqId, HandlerPid),
717
HandlerInfo2 = HandlerInfo#handler_info{starter = undefined,
718
handler = HandlerPid},
719
ets:insert(HandlerDb, HandlerInfo2),
722
[#handler_info{state = State}] when Result =/= ok ->
723
error_report(Profile,
724
"handler (~p, ~w) failed to connect and/or "
727
[HandlerPid, State, ReqId, Result]),
728
?hcri("received connect-and-send error",
729
[{result, Result}, {state, State}]),
730
%% We don't need to send a response to the original caller
731
%% because the handler already sent one in its terminate
733
ets:delete(HandlerDb, ReqId),
737
?hcri("handler successfully started "
738
"for unknown request => canceling",
740
{handler, HandlerPid},
742
httpc_handler:cancel(ReqId, HandlerPid)
746
handle_failed_starting_handler(_StarterPid, ReqId, Error,
747
#state{profile_name = Profile,
748
handler_db = HandlerDb}) ->
749
case ets:lookup(HandlerDb, ReqId) of
750
[#handler_info{state = canceled}] ->
751
error_report(Profile,
752
"failed starting handler for request ~p"
753
"~n Error: ~p", [ReqId, Error]),
754
request_canceled(Profile, ReqId), % Fake signal from handler
755
ets:delete(HandlerDb, ReqId),
758
[#handler_info{from = From}] ->
759
error_report(Profile,
760
"failed starting handler for request ~p"
761
"~n Error: ~p", [ReqId, Error]),
765
{failed_connecting, Reason};
767
{failed_connecting, Error}
769
DummyReq = #request{id = ReqId},
770
httpc_response:send(From, httpc_response:error(DummyReq, Reason2)),
771
ets:delete(HandlerDb, ReqId),
775
error_report(Profile,
776
"failed starting handler for unknown request ~p"
777
"~n Error: ~p", [ReqId, Error]),
782
maybe_handle_terminating_starter(MeybeStarterPid, Reason, HandlerDb) ->
783
Pattern = #handler_info{starter = MeybeStarterPid, _ = '_'},
784
case ets:match_object(HandlerDb, Pattern) of
785
[#handler_info{id = ReqId, from = From, state = initiating}] ->
786
%% The starter process crashed before it could start the
787
%% the handler process, therefor we need to answer the
789
?hcri("starter process crashed bfore starting handler",
790
[{starter, MeybeStarterPid}, {reason, Reason}]),
794
{failed_connecting, Error};
796
{failed_connecting, Reason}
798
DummyReq = #request{id = ReqId},
799
httpc_response:send(From, httpc_response:error(DummyReq, Reason2)),
800
ets:delete(HandlerDb, ReqId),
803
[#handler_info{state = State} = HandlerInfo] ->
804
%% The starter process crashed after the handler was started.
805
%% The handler will answer to the original caller.
806
?hcri("starter process crashed after starting handler",
807
[{starter, MeybeStarterPid}, {reason, Reason}, {state, State}]),
808
HandlerInfo2 = HandlerInfo#handler_info{starter = undefined},
809
ets:insert(HandlerDb, HandlerInfo2),
818
%% Act as an HTTP/0.9 client that does not know anything
819
%% about persistent connections
820
handle_request(#request{settings =
821
#http_options{version = "HTTP/0.9"}} = Request0,
823
Request1 = handle_cookies(generate_request_id(Request0), State),
824
Hdrs0 = Request1#request.headers,
825
Hdrs1 = Hdrs0#http_request_h{connection = undefined},
826
Request2 = Request1#request{headers = Hdrs1},
827
create_handler_starter(Request2, State),
828
{ok, Request2#request.id, State};
831
%% Act as an HTTP/1.0 client that does not
832
%% use persistent connections
833
handle_request(#request{settings =
834
#http_options{version = "HTTP/1.0"}} = Request0,
836
Request1 = handle_cookies(generate_request_id(Request0), State),
837
Hdrs0 = Request1#request.headers,
838
Hdrs1 = Hdrs0#http_request_h{connection = "close"},
839
Request2 = Request1#request{headers = Hdrs1},
840
create_handler_starter(Request2, State),
841
{ok, Request2#request.id, State};
845
handle_request(#request{method = Method,
847
scheme = Scheme} = Request0,
848
#state{options = Opts} = State) ->
849
Request1 = handle_cookies(generate_request_id(Request0), State),
850
SessionType = session_type(Opts),
851
case select_session(Method, Address, Scheme, SessionType, State) of
605
handle_request(#request{settings =
606
#http_options{version = "HTTP/0.9"}} = Request,
608
%% Act as an HTTP/0.9 client that does not know anything
609
%% about persistent connections
611
NewRequest = handle_cookies(generate_request_id(Request), State),
613
(NewRequest#request.headers)#http_request_h{connection
615
start_handler(NewRequest#request{headers = NewHeaders}, State),
616
{reply, {ok, NewRequest#request.id}, State};
618
handle_request(#request{settings =
619
#http_options{version = "HTTP/1.0"}} = Request,
621
%% Act as an HTTP/1.0 client that does not
622
%% use persistent connections
624
NewRequest = handle_cookies(generate_request_id(Request), State),
626
(NewRequest#request.headers)#http_request_h{connection
628
start_handler(NewRequest#request{headers = NewHeaders}, State),
629
{reply, {ok, NewRequest#request.id}, State};
631
handle_request(Request, State = #state{options = Options}) ->
633
NewRequest = handle_cookies(generate_request_id(Request), State),
634
SessionType = session_type(Options),
635
case select_session(Request#request.method,
636
Request#request.address,
637
Request#request.scheme, SessionType, State) of
852
638
{ok, HandlerPid} ->
853
pipeline_or_keep_alive(Request1, HandlerPid, State);
639
pipeline_or_keep_alive(NewRequest, HandlerPid, State);
855
create_handler_starter(Request1, State);
856
{no_session, OpenSessions}
857
when OpenSessions < Opts#options.max_sessions ->
858
create_handler_starter(Request1, State);
641
start_handler(NewRequest, State);
642
{no_session, OpenSessions} when OpenSessions
643
< Options#options.max_sessions ->
644
start_handler(NewRequest, State);
859
645
{no_session, _} ->
860
646
%% Do not start any more persistent connections
861
647
%% towards this server.
862
Hdrs0 = Request1#request.headers,
863
Hdrs1 = Hdrs0#http_request_h{connection = "close"},
864
Request2 = Request1#request{headers = Hdrs1},
865
create_handler_starter(Request2, State)
649
(NewRequest#request.headers)#http_request_h{connection
651
start_handler(NewRequest#request{headers = NewHeaders}, State)
867
{ok, Request1#request.id, State}.
653
{reply, {ok, NewRequest#request.id}, State}.
656
start_handler(Request, State) ->
658
case is_inets_manager() of
660
httpc_handler_sup:start_child([whereis(httpc_handler_sup),
661
Request, State#state.options,
662
State#state.profile_name]);
664
httpc_handler:start_link(self(), Request, State#state.options,
665
State#state.profile_name)
667
ets:insert(State#state.handler_db, {Request#request.id,
668
Pid, Request#request.from}),
669
erlang:monitor(process, Pid).
870
672
select_session(Method, HostPort, Scheme, SessionType,
871
673
#state{options = #options{max_pipeline_length = MaxPipe,
872
674
max_keep_alive_length = MaxKeepAlive},
873
675
session_db = SessionDb}) ->
874
?hcrd("select session", [{session_type, SessionType},
875
{max_pipeline_length, MaxPipe},
676
?hcrd("select session", [{session_type, SessionType},
677
{max_pipeline_length, MaxPipe},
876
678
{max_keep_alive_length, MaxKeepAlive}]),
877
679
case httpc_request:is_idempotent(Method) orelse
878
680
(SessionType =:= keep_alive) of
880
682
%% Look for handlers connecting to this host (HostPort)
881
%% tcp_session with record name field (tcp_session) and
683
%% session with record name field (session) and
882
684
%% socket fields ignored. The fields id (part of: HostPort),
883
685
%% client_close, scheme and type specified.
884
686
%% The fields id (part of: HandlerPid) and queue_length
918
720
?hcrd("select session - found one", [{handler, HandlerPid}]),
922
pipeline_or_keep_alive(#request{id = Id} = Request, HandlerPid, State) ->
923
?hcrd("pipeline of keep-alive", [{id, Id}, {handler, HandlerPid}]),
724
pipeline_or_keep_alive(Request, HandlerPid, State) ->
924
725
case (catch httpc_handler:send(Request, HandlerPid)) of
926
?hcrd("pipeline or keep-alive - successfully sent", []),
927
Entry = #handler_info{id = Id,
928
handler = HandlerPid,
929
state = operational},
930
ets:insert(State#state.handler_db, Entry);
932
_ -> %% timeout pipelining failed
933
?hcrd("pipeline or keep-alive - failed sending -> "
934
"start a new handler", []),
935
create_handler_starter(Request, State)
727
ets:insert(State#state.handler_db, {Request#request.id,
729
Request#request.from});
730
_ -> %timeout pipelining failed
731
start_handler(Request, State)
939
create_handler_starter(#request{socket_opts = SocketOpts} = Request,
940
#state{options = Options} = State)
941
when is_list(SocketOpts) ->
942
%% The user provided us with (override) socket options
943
?hcrt("create handler starter", [{socket_opts, SocketOpts}, {options, Options}]),
944
Options2 = Options#options{socket_opts = SocketOpts},
945
create_handler_starter(Request#request{socket_opts = undefined},
946
State#state{options = Options2});
948
create_handler_starter(#request{id = Id,
949
from = From} = Request,
950
#state{profile_name = ProfileName,
952
handler_db = HandlerDb} = _State) ->
953
?hcrv("create handler starter", [{id, Id}, {profile, ProfileName}]),
954
IsInetsManager = is_inets_manager(),
958
?hcrd("handler starter - start",
960
{profile, ProfileName},
961
{inets_manager, IsInetsManager}]),
963
case IsInetsManager of
965
httpc_handler_sup:start_child(Options,
968
httpc_handler:start_link(Options,
971
?hcrd("handler starter - maybe connect and send",
972
[{id, Id}, {profile, ProfileName}, {result, Result1}]),
976
{started, self(), Id, HandlerPid},
977
ManagerPid ! StartedMessage,
978
Result2 = httpc_handler:connect_and_send(Request,
980
?hcrd("handler starter - connected and sent",
981
[{id, Id}, {profile, ProfileName},
982
{handler, HandlerPid}, {result, Result2}]),
985
self(), Id, HandlerPid, Result2},
986
ManagerPid ! ConnAndSendMessage;
988
StartFailureMessage =
989
{failed_starting_handler, self(), Id, Reason},
990
ManagerPid ! StartFailureMessage;
992
StartFailureMessage =
993
{failed_starting_handler, self(), Id, Result1},
994
ManagerPid ! StartFailureMessage
997
Starter = erlang:spawn_link(StarterFun),
998
?hcrd("create handler starter - started", [{id, Id}, {starter, Starter}]),
999
Entry = #handler_info{id = Id,
1002
state = initiating},
1003
ets:insert(HandlerDb, Entry),
1007
734
is_inets_manager() ->
1008
735
case get('$ancestors') of
1009
736
[httpc_profile_sup | _] ->