diff options
Diffstat (limited to 'lib/inets/src/http_client/httpc.erl')
-rw-r--r-- | lib/inets/src/http_client/httpc.erl | 182 |
1 files changed, 118 insertions, 64 deletions
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index ca186f46a7..fe8e93af1f 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -48,7 +48,7 @@ stop_service/1, services/0, service_info/1]). --include("http_internal.hrl"). +-include_lib("inets/src/http_lib/http_internal.hrl"). -include("httpc_internal.hrl"). -define(DEFAULT_PROFILE, default). @@ -64,17 +64,16 @@ default_profile() -> profile_name(?DEFAULT_PROFILE) -> httpc_manager; +profile_name(Profile) when is_pid(Profile) -> + Profile; profile_name(Profile) -> - profile_name("httpc_manager_", Profile). + Prefix = lists:flatten(io_lib:format("~w_", [?MODULE])), + profile_name(Prefix, Profile). profile_name(Prefix, Profile) when is_atom(Profile) -> list_to_atom(Prefix ++ atom_to_list(Profile)); -profile_name(Prefix, Profile) when is_pid(Profile) -> - ProfileStr0 = - string:strip(string:strip(erlang:pid_to_list(Profile), left, $<), right, $>), - F = fun($.) -> $_; (X) -> X end, - ProfileStr = [F(C) || C <- ProfileStr0], - list_to_atom(Prefix ++ "pid_" ++ ProfileStr). +profile_name(_Prefix, Profile) when is_pid(Profile) -> + Profile. %%-------------------------------------------------------------------------- @@ -104,14 +103,22 @@ request(Url, Profile) -> %% HTTPOptions - [HttpOption] %% HTTPOption - {timeout, Time} | {connect_timeout, Time} | %% {ssl, SSLOptions} | {proxy_auth, {User, Password}} -%% Ssloptions = [SSLOption] -%% SSLOption = {verify, code()} | {depth, depth()} | {certfile, path()} | +%% Ssloptions = ssl_options() | +%% {ssl, ssl_options()} | +%% {ossl, ssl_options()} | +%% {essl, ssl_options()} +%% ssl_options() = [ssl_option()] +%% ssl_option() = {verify, code()} | +%% {depth, depth()} | +%% {certfile, path()} | %% {keyfile, path()} | {password, string()} | {cacertfile, path()} | %% {ciphers, string()} %% Options - [Option] -%% Option - {sync, Boolean} | {body_format, BodyFormat} | -%% {full_result, Boolean} | {stream, To} | -%% {headers_as_is, Boolean} +%% Option - {sync, Boolean} | +%% {body_format, BodyFormat} | +%% {full_result, Boolean} | +%% {stream, To} | +%% {headers_as_is, Boolean} %% StatusLine = {HTTPVersion, StatusCode, ReasonPhrase}</v> %% HTTPVersion = string() %% StatusCode = integer() @@ -120,7 +127,10 @@ request(Url, Profile) -> %% Header = {Field, Value} %% Field = string() %% Value = string() -%% Body = string() | binary() - HTLM-code +%% Body = string() | binary() | {fun(SendAcc) -> SendFunResult, SendAcc} | +%% {chunkify, fun(SendAcc) -> SendFunResult, SendAcc} - HTLM-code +%% SendFunResult = eof | {ok, iolist(), NewSendAcc} +%% SendAcc = NewSendAcc = term() %% %% Description: Sends a HTTP-request. The function can be both %% syncronus and asynchronous in the later case the function will @@ -246,7 +256,7 @@ set_option(Key, Value, Profile) -> %% Description: Store the cookies from <SetCookieHeaders> %% in the cookie database %% for the profile <Profile>. This function shall be used when the option -%% cookie is set to verify. +%% cookies is set to verify. %%------------------------------------------------------------------------- store_cookies(SetCookieHeaders, Url) -> store_cookies(SetCookieHeaders, Url, default_profile()). @@ -420,14 +430,33 @@ service_info(Pid) -> handle_request(Method, Url, {Scheme, UserInfo, Host, Port, Path, Query}, - Headers, ContentType, Body, + Headers0, ContentType, Body0, HTTPOptions0, Options0, Profile) -> - Started = http_util:timestamp(), - NewHeaders = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers], + Started = http_util:timestamp(), + NewHeaders0 = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers0], try begin + ?hcrt("begin processing", [{started, Started}, + {new_headers, NewHeaders0}]), + + {NewHeaders, Body} = + case Body0 of + {chunkify, ProcessBody, Acc} + when is_function(ProcessBody, 1) -> + NewHeaders1 = ensure_chunked_encoding(NewHeaders0), + Body1 = {mk_chunkify_fun(ProcessBody), Acc}, + {NewHeaders1, Body1}; + {ProcessBody, _} + when is_function(ProcessBody, 1) -> + {NewHeaders0, Body0}; + _ when is_list(Body0) orelse is_binary(Body0) -> + {NewHeaders0, Body0}; + _ -> + throw({error, {bad_body, Body0}}) + end, + HTTPOptions = http_options(HTTPOptions0), Options = request_options(Options0), Sync = proplists:get_value(sync, Options), @@ -436,52 +465,84 @@ handle_request(Method, Url, HeadersRecord = header_record(NewHeaders, Host2, HTTPOptions), Receiver = proplists:get_value(receiver, Options), SocketOpts = proplists:get_value(socket_opts, Options), - MaybeEscPath = maybe_url_encode(HTTPOptions, Path), - MaybeEscQuery = maybe_url_encode(HTTPOptions, Query), - AbsUri = maybe_url_encode(HTTPOptions, Url), + MaybeEscPath = maybe_encode_uri(HTTPOptions, Path), + MaybeEscQuery = maybe_encode_uri(HTTPOptions, Query), + AbsUri = maybe_encode_uri(HTTPOptions, Url), + Request = #request{from = Receiver, scheme = Scheme, address = {Host, Port}, - path = MaybeEscPath, - pquery = MaybeEscQuery, + path = MaybeEscPath, + pquery = MaybeEscQuery, method = Method, headers = HeadersRecord, content = {ContentType, Body}, settings = HTTPOptions, - abs_uri = AbsUri, + abs_uri = AbsUri, userinfo = UserInfo, stream = Stream, - headers_as_is = headers_as_is(Headers, Options), + headers_as_is = headers_as_is(Headers0, Options), socket_opts = SocketOpts, started = Started}, + case httpc_manager:request(Request, profile_name(Profile)) of {ok, RequestId} -> handle_answer(RequestId, Sync, Options); {error, Reason} -> + ?hcrd("request failed", [{reason, Reason}]), {error, Reason} end end catch error:{noproc, _} -> + ?hcrv("noproc", [{profile, Profile}]), {error, {not_started, Profile}}; throw:Error -> + ?hcrv("throw", [{error, Error}]), Error end. -maybe_url_encode(#http_options{url_encode = true}, URI) -> +ensure_chunked_encoding(Hdrs) -> + Key = "transfer-encoding", + lists:keystore(Key, 1, Hdrs, {Key, "chunked"}). + +maybe_encode_uri(#http_options{url_encode = true}, URI) -> http_uri:encode(URI); -maybe_url_encode(_, URI) -> +maybe_encode_uri(_, URI) -> URI. +mk_chunkify_fun(ProcessBody) -> + fun(eof_body) -> + eof; + (Acc) -> + case ProcessBody(Acc) of + eof -> + {ok, <<"0\r\n\r\n">>, eof_body}; + {ok, Data, NewAcc} -> + Chunk = [ + integer_to_list(iolist_size(Data), 16), + "\r\n", + Data, + "\r\n"], + {ok, Chunk, NewAcc} + end + end. + + handle_answer(RequestId, false, _) -> {ok, RequestId}; handle_answer(RequestId, true, Options) -> receive {http, {RequestId, saved_to_file}} -> + ?hcrt("received saved-to-file", [{request_id, RequestId}]), {ok, saved_to_file}; {http, {RequestId, {_,_,_} = Result}} -> + ?hcrt("received answer", [{request_id, RequestId}, + {result, Result}]), return_answer(Options, Result); {http, {RequestId, {error, Reason}}} -> + ?hcrt("received error", [{request_id, RequestId}, + {reason, Reason}]), {error, Reason} end. @@ -490,9 +551,7 @@ return_answer(Options, {{"HTTP/0.9",_,_}, _, BinBody}) -> {ok, Body}; return_answer(Options, {StatusLine, Headers, BinBody}) -> - Body = maybe_format_body(BinBody, Options), - case proplists:get_value(full_result, Options, true) of true -> {ok, {StatusLine, Headers, Body}}; @@ -579,14 +638,16 @@ http_options_default() -> (_) -> error end, - AutoRedirectPost = fun(Value) when (Value =:= true) orelse - (Value =:= false) -> - {ok, Value}; - (_) -> - error - end, + AutoRedirectPost = boolfun(), + SslPost = fun(Value) when is_list(Value) -> - {ok, Value}; + {ok, {?HTTP_DEFAULT_SSL_KIND, Value}}; + ({ssl, SslOptions}) when is_list(SslOptions) -> + {ok, {?HTTP_DEFAULT_SSL_KIND, SslOptions}}; + ({ossl, SslOptions}) when is_list(SslOptions) -> + {ok, {ossl, SslOptions}}; + ({essl, SslOptions}) when is_list(SslOptions) -> + {ok, {essl, SslOptions}}; (_) -> error end, @@ -596,12 +657,8 @@ http_options_default() -> (_) -> error end, - RelaxedPost = fun(Value) when (Value =:= true) orelse - (Value =:= false) -> - {ok, Value}; - (_) -> - error - end, + RelaxedPost = boolfun(), + ConnTimeoutPost = fun(Value) when is_integer(Value) andalso (Value >= 0) -> {ok, Value}; @@ -610,33 +667,30 @@ http_options_default() -> (_) -> error end, - - UrlDecodePost = fun(Value) when (Value =:= true) orelse - (Value =:= false) -> - {ok, Value}; - (_) -> - error - end, + + UrlDecodePost = boolfun(), [ - {version, {value, "HTTP/1.1"}, #http_options.version, VersionPost}, - {timeout, {value, ?HTTP_REQUEST_TIMEOUT}, #http_options.timeout, TimeoutPost}, - {autoredirect, {value, true}, #http_options.autoredirect, AutoRedirectPost}, - {ssl, {value, []}, #http_options.ssl, SslPost}, - {proxy_auth, {value, undefined}, #http_options.proxy_auth, ProxyAuthPost}, - {relaxed, {value, false}, #http_options.relaxed, RelaxedPost}, - %% this field has to be *after* the timeout field (as that field is used for the default value) - {connect_timeout, {field, #http_options.timeout}, #http_options.connect_timeout, ConnTimeoutPost}, - {url_encode, {value, false}, #http_options.url_encode, UrlDecodePost} + {version, {value, "HTTP/1.1"}, #http_options.version, VersionPost}, + {timeout, {value, ?HTTP_REQUEST_TIMEOUT}, #http_options.timeout, TimeoutPost}, + {autoredirect, {value, true}, #http_options.autoredirect, AutoRedirectPost}, + {ssl, {value, {?HTTP_DEFAULT_SSL_KIND, []}}, #http_options.ssl, SslPost}, + {proxy_auth, {value, undefined}, #http_options.proxy_auth, ProxyAuthPost}, + {relaxed, {value, false}, #http_options.relaxed, RelaxedPost}, + {url_encode, {value, false}, #http_options.url_encode, UrlDecodePost}, + %% this field has to be *after* the timeout option (as that field is used for the default value) + {connect_timeout, {field, #http_options.timeout}, #http_options.connect_timeout, ConnTimeoutPost} ]. +boolfun() -> + fun(Value) when (Value =:= true) orelse + (Value =:= false) -> + {ok, Value}; + (_) -> + error + end. request_options_defaults() -> - VerifyBoolean = - fun(Value) when ((Value =:= true) orelse (Value =:= false)) -> - ok; - (_) -> - error - end, + VerifyBoolean = boolfun(), VerifySync = VerifyBoolean, |