diff options
Diffstat (limited to 'lib/inets/src')
-rw-r--r-- | lib/inets/src/ftp/ftp.erl | 259 | ||||
-rw-r--r-- | lib/inets/src/http_client/httpc.erl | 82 | ||||
-rw-r--r-- | lib/inets/src/http_client/httpc_handler.erl | 18 | ||||
-rw-r--r-- | lib/inets/src/http_client/httpc_request.erl | 102 | ||||
-rw-r--r-- | lib/inets/src/http_lib/http_util.erl | 18 | ||||
-rw-r--r-- | lib/inets/src/http_server/httpd_file.erl | 2 | ||||
-rw-r--r-- | lib/inets/src/http_server/httpd_log.erl | 2 | ||||
-rw-r--r-- | lib/inets/src/http_server/httpd_request_handler.erl | 2 | ||||
-rw-r--r-- | lib/inets/src/http_server/httpd_util.erl | 38 | ||||
-rw-r--r-- | lib/inets/src/http_server/mod_esi.erl | 4 | ||||
-rw-r--r-- | lib/inets/src/inets_app/inets.appup.src | 56 |
11 files changed, 498 insertions, 85 deletions
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 5ad74851c8..fe6cb0c191 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -92,6 +92,12 @@ }). +-type shortage_reason() :: 'etnospc' | 'epnospc'. +-type restriction_reason() :: 'epath' | 'efnamena' | 'elogin' | 'enotbinary'. +-type common_reason() :: 'econn' | 'eclosed' | term(). +-type file_write_error_reason() :: term(). % See file:write for more info + + %%%========================================================================= %%% API - CLIENT FUNCTIONS %%%========================================================================= @@ -106,6 +112,9 @@ %% Description: Start an ftp client and connect to a host. %%-------------------------------------------------------------------------- +-spec open(Host :: string() | inet:ip_address()) -> + {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}. + %% <BACKWARD-COMPATIBILLITY> open({option_list, Options}) when is_list(Options) -> try @@ -126,6 +135,9 @@ open({option_list, Options}) when is_list(Options) -> open(Host) -> open(Host, []). +-spec open(Host :: string() | inet:ip_address(), Opts :: list()) -> + {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}. + %% <BACKWARD-COMPATIBILLITY> open(Host, Port) when is_integer(Port) -> open(Host, [{port, Port}]); @@ -161,12 +173,24 @@ open(Host, Opts) when is_list(Opts) -> %% %% Description: Login with or without a supplied account name. %%-------------------------------------------------------------------------- +-spec user(Pid :: pid(), + User :: string(), + Pass :: string()) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + user(Pid, User, Pass) -> call(Pid, {user, User, Pass}, atom). +-spec user(Pid :: pid(), + User :: string(), + Pass :: string(), + Acc :: string()) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + user(Pid, User, Pass, Acc) -> call(Pid, {user, User, Pass, Acc}, atom). + %%-------------------------------------------------------------------------- %% account(Pid, Acc) -> ok | {error, eacct} %% Pid = pid() @@ -174,9 +198,14 @@ user(Pid, User, Pass, Acc) -> %% %% Description: Set a user Account. %%-------------------------------------------------------------------------- + +-spec account(Pid :: pid(), Acc :: string()) -> + 'ok' | {'error', Reason :: 'eacct' | common_reason()}. + account(Pid, Acc) -> call(Pid, {account, Acc}, atom). + %%-------------------------------------------------------------------------- %% pwd(Pid) -> {ok, Dir} | {error, elogin} | {error, econn} %% Pid = pid() @@ -184,19 +213,30 @@ account(Pid, Acc) -> %% %% Description: Get the current working directory at remote server. %%-------------------------------------------------------------------------- + +-spec pwd(Pid :: pid()) -> + {'ok', Dir :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + pwd(Pid) -> call(Pid, pwd, ctrl). + %%-------------------------------------------------------------------------- -%% lpwd(Pid) -> {ok, Dir} | {error, elogin} +%% lpwd(Pid) -> {ok, Dir} %% Pid = pid() %% Dir = string() %% %% Description: Get the current working directory at local server. %%-------------------------------------------------------------------------- + +-spec lpwd(Pid :: pid()) -> + {'ok', Dir :: string()}. + lpwd(Pid) -> call(Pid, lpwd, string). + %%-------------------------------------------------------------------------- %% cd(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid() @@ -204,9 +244,14 @@ lpwd(Pid) -> %% %% Description: Change current working directory at remote server. %%-------------------------------------------------------------------------- + +-spec cd(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + cd(Pid, Dir) -> call(Pid, {cd, Dir}, atom). + %%-------------------------------------------------------------------------- %% lcd(Pid, Dir) -> ok | {error, epath} %% Pid = pid() @@ -214,9 +259,14 @@ cd(Pid, Dir) -> %% %% Description: Change current working directory for the local client. %%-------------------------------------------------------------------------- + +-spec lcd(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason()}. + lcd(Pid, Dir) -> call(Pid, {lcd, Dir}, string). + %%-------------------------------------------------------------------------- %% ls(Pid) -> Result %% ls(Pid, <Dir>) -> Result @@ -229,11 +279,22 @@ lcd(Pid, Dir) -> %% %% Description: Returns a list of files in long format. %%-------------------------------------------------------------------------- + +-spec ls(Pid :: pid()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + ls(Pid) -> ls(Pid, ""). + +-spec ls(Pid :: pid(), Dir :: string()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + ls(Pid, Dir) -> call(Pid, {dir, long, Dir}, string). + %%-------------------------------------------------------------------------- %% nlist(Pid) -> Result %% nlist(Pid, Pathname) -> Result @@ -246,21 +307,37 @@ ls(Pid, Dir) -> %% %% Description: Returns a list of files in short format %%-------------------------------------------------------------------------- + +-spec nlist(Pid :: pid()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + nlist(Pid) -> nlist(Pid, ""). + +-spec nlist(Pid :: pid(), Pathname :: string()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + nlist(Pid, Dir) -> call(Pid, {dir, short, Dir}, string). + %%-------------------------------------------------------------------------- -%% rename(Pid, CurrFile, NewFile) -> ok | {error, epath} | {error, elogin} -%% | {error, econn} +%% rename(Pid, Old, New) -> ok | {error, epath} | {error, elogin} +%% | {error, econn} %% Pid = pid() %% CurrFile = NewFile = string() %% %% Description: Rename a file at remote server. %%-------------------------------------------------------------------------- -rename(Pid, CurrFile, NewFile) -> - call(Pid, {rename, CurrFile, NewFile}, string). + +-spec rename(Pid :: pid(), Old :: string(), New :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + +rename(Pid, Old, New) -> + call(Pid, {rename, Old, New}, string). + %%-------------------------------------------------------------------------- %% delete(Pid, File) -> ok | {error, epath} | {error, elogin} | @@ -270,9 +347,14 @@ rename(Pid, CurrFile, NewFile) -> %% %% Description: Remove file at remote server. %%-------------------------------------------------------------------------- + +-spec delete(Pid :: pid(), File :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + delete(Pid, File) -> call(Pid, {delete, File}, string). + %%-------------------------------------------------------------------------- %% mkdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid(), @@ -280,9 +362,14 @@ delete(Pid, File) -> %% %% Description: Make directory at remote server. %%-------------------------------------------------------------------------- + +-spec mkdir(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + mkdir(Pid, Dir) -> call(Pid, {mkdir, Dir}, atom). + %%-------------------------------------------------------------------------- %% rmdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid(), @@ -290,9 +377,14 @@ mkdir(Pid, Dir) -> %% %% Description: Remove directory at remote server. %%-------------------------------------------------------------------------- + +-spec rmdir(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + rmdir(Pid, Dir) -> call(Pid, {rmdir, Dir}, atom). + %%-------------------------------------------------------------------------- %% type(Pid, Type) -> ok | {error, etype} | {error, elogin} | {error, econn} %% Pid = pid() @@ -300,23 +392,41 @@ rmdir(Pid, Dir) -> %% %% Description: Set transfer type. %%-------------------------------------------------------------------------- + +-spec type(Pid :: pid(), Type :: ascii | binary) -> + 'ok' | + {'error', Reason :: 'etype' | restriction_reason() | common_reason()}. + type(Pid, Type) -> call(Pid, {type, Type}, atom). + %%-------------------------------------------------------------------------- -%% recv(Pid, RemoteFileName <LocalFileName>) -> ok | {error, epath} | +%% recv(Pid, RemoteFileName [, LocalFileName]) -> ok | {error, epath} | %% {error, elogin} | {error, econn} %% Pid = pid() %% RemoteFileName = LocalFileName = string() %% %% Description: Transfer file from remote server. %%-------------------------------------------------------------------------- + +-spec recv(Pid :: pid(), RemoteFileName :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | + common_reason() | + file_write_error_reason()}. + recv(Pid, RemotFileName) -> recv(Pid, RemotFileName, RemotFileName). +-spec recv(Pid :: pid(), + RemoteFileName :: string(), + LocalFileName :: string()) -> + 'ok' | {'error', Reason :: term()}. + recv(Pid, RemotFileName, LocalFileName) -> call(Pid, {recv, RemotFileName, LocalFileName}, atom). + %%-------------------------------------------------------------------------- %% recv_bin(Pid, RemoteFile) -> {ok, Bin} | {error, epath} | {error, elogin} %% | {error, econn} @@ -326,9 +436,16 @@ recv(Pid, RemotFileName, LocalFileName) -> %% %% Description: Transfer file from remote server into binary. %%-------------------------------------------------------------------------- + +-spec recv_bin(Pid :: pid(), + RemoteFile :: string()) -> + {'ok', Bin :: binary()} | + {'error', Reason :: restriction_reason() | common_reason()}. + recv_bin(Pid, RemoteFile) -> call(Pid, {recv_bin, RemoteFile}, bin). + %%-------------------------------------------------------------------------- %% recv_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath} %% | {error, econn} @@ -337,9 +454,15 @@ recv_bin(Pid, RemoteFile) -> %% %% Description: Start receive of chunks of remote file. %%-------------------------------------------------------------------------- + +-spec recv_chunk_start(Pid :: pid(), + RemoteFile :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + recv_chunk_start(Pid, RemoteFile) -> call(Pid, {recv_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% recv_chunk(Pid, RemoteFile) -> ok | {ok, Bin} | {error, Reason} %% Pid = pid() @@ -347,24 +470,47 @@ recv_chunk_start(Pid, RemoteFile) -> %% %% Description: Transfer file from remote server into binary in chunks %%-------------------------------------------------------------------------- + +-spec recv_chunk(Pid :: pid()) -> + 'ok' | + {'ok', Bin :: binary()} | + {'error', Reason :: restriction_reason() | common_reason()}. + recv_chunk(Pid) -> call(Pid, recv_chunk, atom). + %%-------------------------------------------------------------------------- -%% send(Pid, LocalFileName <RemotFileName>) -> ok | {error, epath} -%% | {error, elogin} -%% | {error, econn} +%% send(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath} +%% | {error, elogin} +%% | {error, econn} %% Pid = pid() %% LocalFileName = RemotFileName = string() %% %% Description: Transfer file to remote server. %%-------------------------------------------------------------------------- + +-spec send(Pid :: pid(), LocalFileName :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send(Pid, LocalFileName) -> send(Pid, LocalFileName, LocalFileName). +-spec send(Pid :: pid(), + LocalFileName :: string(), + RemoteFileName :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send(Pid, LocalFileName, RemotFileName) -> call(Pid, {send, LocalFileName, RemotFileName}, atom). + %%-------------------------------------------------------------------------- %% send_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin} %% | {error, enotbinary} | {error, econn} @@ -374,11 +520,19 @@ send(Pid, LocalFileName, RemotFileName) -> %% %% Description: Transfer a binary to a remote file. %%-------------------------------------------------------------------------- + +-spec send_bin(Pid :: pid(), Bin :: binary(), RemoteFile :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> call(Pid, {send_bin, Bin, RemoteFile}, atom); send_bin(_Pid, _Bin, _RemoteFile) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% send_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath} %% | {error, econn} @@ -387,9 +541,14 @@ send_bin(_Pid, _Bin, _RemoteFile) -> %% %% Description: Start transfer of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk_start(Pid :: pid(), RemoteFile :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + send_chunk_start(Pid, RemoteFile) -> call(Pid, {send_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% append_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | %% {error, epath} | {error, econn} @@ -398,9 +557,14 @@ send_chunk_start(Pid, RemoteFile) -> %% %% Description: Start append chunks of data to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk_start(Pid :: pid(), RemoteFile :: string()) -> + 'ok' | {'error', Reason :: term()}. + append_chunk_start(Pid, RemoteFile) -> call(Pid, {append_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% send_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary} %% | {error, echunk} | {error, econn} @@ -409,11 +573,19 @@ append_chunk_start(Pid, RemoteFile) -> %% %% Purpose: Send chunk to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk(Pid :: pid(), Bin :: binary()) -> + 'ok' | + {'error', Reason :: 'echunk' | + restriction_reason() | + common_reason()}. + send_chunk(Pid, Bin) when is_binary(Bin) -> call(Pid, {transfer_chunk, Bin}, atom); send_chunk(_Pid, _Bin) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% append_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary} %% | {error, echunk} | {error, econn} @@ -422,11 +594,19 @@ send_chunk(_Pid, _Bin) -> %% %% Description: Append chunk to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk(Pid :: pid(), Bin :: binary()) -> + 'ok' | + {'error', Reason :: 'echunk' | + restriction_reason() | + common_reason()}. + append_chunk(Pid, Bin) when is_binary(Bin) -> call(Pid, {transfer_chunk, Bin}, atom); append_chunk(_Pid, _Bin) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% send_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk} %% | {error, econn} @@ -434,9 +614,17 @@ append_chunk(_Pid, _Bin) -> %% %% Description: End sending of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk_end(Pid :: pid()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send_chunk_end(Pid) -> call(Pid, chunk_end, atom). + %%-------------------------------------------------------------------------- %% append_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk} %% | {error, econn} @@ -444,23 +632,47 @@ send_chunk_end(Pid) -> %% %% Description: End appending of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk_end(Pid :: pid()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + append_chunk_end(Pid) -> call(Pid, chunk_end, atom). + %%-------------------------------------------------------------------------- -%% append(Pid, LocalFileName, RemotFileName) -> ok | {error, epath} -%% | {error, elogin} | {error, econn} +%% append(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath} +%% | {error, elogin} +%% | {error, econn} %% Pid = pid() %% LocalFileName = RemotFileName = string() %% %% Description: Append the local file to the remote file %%-------------------------------------------------------------------------- + +-spec append(Pid :: pid(), LocalFileName :: string()) -> + 'ok' | + {'error', Reason :: 'epath' | + 'elogin' | + 'etnospc' | + 'epnospc' | + 'efnamena' | common_reason()}. + append(Pid, LocalFileName) -> append(Pid, LocalFileName, LocalFileName). +-spec append(Pid :: pid(), + LocalFileName :: string(), + RemoteFileName :: string()) -> + 'ok' | {'error', Reason :: term()}. + append(Pid, LocalFileName, RemotFileName) -> call(Pid, {append, LocalFileName, RemotFileName}, atom). + %%-------------------------------------------------------------------------- %% append_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin} %% | {error, enotbinary} | {error, econn} @@ -470,27 +682,44 @@ append(Pid, LocalFileName, RemotFileName) -> %% %% Purpose: Append a binary to a remote file. %%-------------------------------------------------------------------------- + +-spec append_bin(Pid :: pid(), + Bin :: binary(), + RemoteFile :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + append_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> call(Pid, {append_bin, Bin, RemoteFile}, atom); append_bin(_Pid, _Bin, _RemoteFile) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- -%% quote(Pid, Cmd) -> ok +%% quote(Pid, Cmd) -> list() %% Pid = pid() %% Cmd = string() %% %% Description: Send arbitrary ftp command. %%-------------------------------------------------------------------------- + +-spec quote(Pid :: pid(), Cmd :: string()) -> list(). + quote(Pid, Cmd) when is_list(Cmd) -> call(Pid, {quote, Cmd}, atom). + %%-------------------------------------------------------------------------- %% close(Pid) -> ok %% Pid = pid() %% %% Description: End the ftp session. %%-------------------------------------------------------------------------- + +-spec close(Pid :: pid()) -> 'ok'. + close(Pid) -> cast(Pid, close), ok. @@ -502,9 +731,13 @@ close(Pid) -> %% %% Description: Return diagnostics. %%-------------------------------------------------------------------------- + +-spec formaterror(Tag :: term()) -> string(). + formaterror(Tag) -> ftp_response:error_string(Tag). + info(Pid) -> call(Pid, info, list). diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index 04fae13b20..b70b16f57f 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -126,7 +126,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 @@ -426,26 +429,44 @@ 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), Stream = proplists:get_value(stream, Options), Host2 = header_host(Scheme, Host, Port), HeadersRecord = header_record(NewHeaders, Host2, HTTPOptions), - Receiver = proplists:get_value(receiver, Options), - SocketOpts = proplists:get_value(socket_opts, Options), - UrlEncodeBool = HTTPOptions#http_options.url_encode, - MaybeEscPath = url_encode(Path, UrlEncodeBool), - MaybeEscQuery = url_encode(Query, UrlEncodeBool), - AbsUri = url_encode(Url, UrlEncodeBool), + Receiver = proplists:get_value(receiver, Options), + SocketOpts = proplists:get_value(socket_opts, Options), + MaybeEscPath = maybe_encode_uri(HTTPOptions, Path), + MaybeEscQuery = maybe_encode_uri(HTTPOptions, Query), + AbsUri = maybe_encode_uri(HTTPOptions, Url), Request = #request{from = Receiver, scheme = Scheme, @@ -458,38 +479,71 @@ handle_request(Method, Url, settings = HTTPOptions, abs_uri = AbsUri, userinfo = UserInfo, - stream = Stream, - headers_as_is = headers_as_is(Headers, Options), + stream = Stream, + 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. -url_encode(URI, true) -> +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); -url_encode(URI, false) -> +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} -> + {ok, mk_chunk_bin(Data), NewAcc} + end + end. + +mk_chunk_bin(Data) -> + Bin = iolist_to_binary(Data), + iolist_to_binary([hex_size(Bin), "\r\n", Bin, "\r\n"]). + +hex_size(Bin) -> + hd(io_lib:format("~.16B", [size(Bin)])). + + 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. diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index cb6f3e2841..5e22400fe0 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -1724,9 +1724,27 @@ handle_verbose(_) -> %% ok. +send_raw(#session{socket = Socket, socket_type = SocketType}, + {ProcessBody, Acc}) when is_function(ProcessBody, 1) -> + ?hcrt("send raw", [{acc, Acc}]), + send_raw(SocketType, Socket, ProcessBody, Acc); send_raw(#session{socket = Socket, socket_type = SocketType}, Body) -> http_transport:send(SocketType, Socket, Body). +send_raw(SocketType, Socket, ProcessBody, Acc) -> + case ProcessBody(Acc) of + eof -> + ok; + {ok, Data, NewAcc} -> + DataBin = iolist_to_binary(Data), + ?hcrd("send", [{data, DataBin}]), + case http_transport:send(SocketType, Socket, DataBin) of + ok -> + send_raw(SocketType, Socket, ProcessBody, NewAcc); + Error -> + Error + end + end. call(Msg, Pid) -> diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl index d4df97ad40..0d602adb11 100644 --- a/lib/inets/src/http_client/httpc_request.erl +++ b/lib/inets/src/http_client/httpc_request.erl @@ -79,37 +79,65 @@ send(SendAddr, Socket, SocketType, {settings, HttpOptions}, {userinfo, UserInfo}]), - TmpHeaders = handle_user_info(UserInfo, Headers), + TmpHdrs = handle_user_info(UserInfo, Headers), - {TmpHeaders2, Body} = - post_data(Method, TmpHeaders, Content, HeadersAsIs), + {TmpHdrs2, Body} = post_data(Method, TmpHdrs, Content, HeadersAsIs), - {NewHeaders, Uri} = case Address of - SendAddr -> - {TmpHeaders2, Path ++ Query}; - _Proxy -> - TmpHeaders3 = - handle_proxy(HttpOptions, TmpHeaders2), - {TmpHeaders3, AbsUri} - end, - - FinalHeaders = case NewHeaders of - HeaderList when is_list(HeaderList) -> - http_headers(HeaderList, []); - _ -> - http_request:http_headers(NewHeaders) - end, + {NewHeaders, Uri} = + case Address of + SendAddr -> + {TmpHdrs2, Path ++ Query}; + _Proxy -> + TmpHdrs3 = handle_proxy(HttpOptions, TmpHdrs2), + {TmpHdrs3, AbsUri} + end, + + FinalHeaders = + case NewHeaders of + HeaderList when is_list(HeaderList) -> + http_headers(HeaderList, []); + _ -> + http_request:http_headers(NewHeaders) + end, Version = HttpOptions#http_options.version, - Message = [method(Method), " ", Uri, " ", - version(Version), ?CRLF, - headers(FinalHeaders, Version), ?CRLF, Body], + do_send_body(SocketType, Socket, Method, Uri, Version, FinalHeaders, Body). + + +do_send_body(SocketType, Socket, Method, Uri, Version, Headers, + {ProcessBody, Acc}) when is_function(ProcessBody, 1) -> + ?hcrt("send", [{acc, Acc}]), + case do_send_body(SocketType, Socket, Method, Uri, Version, Headers, []) of + ok -> + do_send_body(SocketType, Socket, ProcessBody, Acc); + Error -> + Error + end; +do_send_body(SocketType, Socket, Method, Uri, Version, Headers, Body) -> + ?hcrt("create message", [{body, Body}]), + Message = [method(Method), " ", Uri, " ", + version(Version), ?CRLF, + headers(Headers, Version), ?CRLF, Body], ?hcrd("send", [{message, Message}]), - http_transport:send(SocketType, Socket, lists:append(Message)). +do_send_body(SocketType, Socket, ProcessBody, Acc) -> + case ProcessBody(Acc) of + eof -> + ok; + {ok, Data, NewAcc} -> + DataBin = iolist_to_binary(Data), + ?hcrd("send", [{data, DataBin}]), + case http_transport:send(SocketType, Socket, DataBin) of + ok -> + do_send_body(SocketType, Socket, ProcessBody, NewAcc); + Error -> + Error + end + end. + %%------------------------------------------------------------------------- %% is_idempotent(Method) -> @@ -161,7 +189,6 @@ is_client_closing(Headers) -> %%%======================================================================== post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) when (Method =:= post) orelse (Method =:= put) -> - ContentLength = body_length(Body), NewBody = case Headers#http_request_h.expect of "100-continue" -> ""; @@ -170,14 +197,22 @@ post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) end, NewHeaders = case HeadersAsIs of - [] -> - Headers#http_request_h{'content-type' = - ContentType, - 'content-length' = - ContentLength}; - _ -> - HeadersAsIs - end, + [] -> + Headers#http_request_h{ + 'content-type' = ContentType, + 'content-length' = case body_length(Body) of + undefined -> + % on upload streaming the caller must give a + % value to the Content-Length header + % (or use chunked Transfer-Encoding) + Headers#http_request_h.'content-length'; + Len when is_list(Len) -> + Len + end + }; + _ -> + HeadersAsIs + end, {NewHeaders, NewBody}; @@ -190,7 +225,10 @@ body_length(Body) when is_binary(Body) -> integer_to_list(size(Body)); body_length(Body) when is_list(Body) -> - integer_to_list(length(Body)). + integer_to_list(length(Body)); + +body_length({DataFun, _Acc}) when is_function(DataFun, 1) -> + undefined. method(Method) -> http_util:to_upper(atom_to_list(Method)). diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl index 4f1147176c..5e6b69ac5e 100644 --- a/lib/inets/src/http_lib/http_util.erl +++ b/lib/inets/src/http_lib/http_util.erl @@ -25,7 +25,8 @@ hexlist_to_integer/1, integer_to_hexlist/1, convert_month/1, is_hostname/1, - timestamp/0, timeout/2 + timestamp/0, timeout/2, + html_encode/1 ]). @@ -187,6 +188,13 @@ timeout(Timeout, Started) -> end. +html_encode(Chars) -> + Reserved = sets:from_list([$&, $<, $>, $\", $', $/]), + lists:append(lists:map(fun(Char) -> + char_to_html_entity(Char, Reserved) + end, Chars)). + + %%%======================================================================== %%% Internal functions %%%======================================================================== @@ -235,3 +243,11 @@ convert_to_ascii([Num | Reversed], Number) convert_to_ascii([Num | Reversed], Number) when (Num > 9) andalso (Num < 16) -> convert_to_ascii(Reversed, [Num + 55 | Number]). + +char_to_html_entity(Char, Reserved) -> + case sets:is_element(Char, Reserved) of + true -> + "&#" ++ integer_to_list(Char) ++ ";"; + false -> + [Char] + end. diff --git a/lib/inets/src/http_server/httpd_file.erl b/lib/inets/src/http_server/httpd_file.erl index dc1dae8bc9..ccc1f7874a 100644 --- a/lib/inets/src/http_server/httpd_file.erl +++ b/lib/inets/src/http_server/httpd_file.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/inets/src/http_server/httpd_log.erl b/lib/inets/src/http_server/httpd_log.erl index 4b0f49d560..db1e2c627a 100644 --- a/lib/inets/src/http_server/httpd_log.erl +++ b/lib/inets/src/http_server/httpd_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index 31ec416b2d..c3b47ce390 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl index 789f12652b..c1aff65d5e 100644 --- a/lib/inets/src/http_server/httpd_util.erl +++ b/lib/inets/src/http_server/httpd_util.erl @@ -181,7 +181,7 @@ message(304, _URL,_) -> message(400,none,_) -> "Your browser sent a query that this server could not understand."; message(400,Msg,_) -> - "Your browser sent a query that this server could not understand. "++ maybe_encode(Msg); + "Your browser sent a query that this server could not understand. "++ http_util:html_encode(Msg); message(401,none,_) -> "This server could not verify that you are authorized to access the document you @@ -190,48 +190,48 @@ credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required."; message(403,RequestURI,_) -> - "You don't have permission to access "++ maybe_encode(RequestURI) ++" on this server."; + "You don't have permission to access "++ http_util:html_encode(RequestURI) ++" on this server."; message(404,RequestURI,_) -> - "The requested URL " ++ maybe_encode(RequestURI) ++ " was not found on this server."; + "The requested URL " ++ http_util:html_encode(RequestURI) ++ " was not found on this server."; message(408, Timeout, _) -> Timeout; message(412,none,_) -> - "The requested preconditions where false"; + "The requested preconditions were false"; message(413, Reason,_) -> - "Entity: " ++ Reason; + "Entity: " ++ http_util:html_encode(Reason); message(414,ReasonPhrase,_) -> - "Message "++ ReasonPhrase ++"."; + "Message "++ http_util:html_encode(ReasonPhrase) ++"."; message(416,ReasonPhrase,_) -> - ReasonPhrase; + http_util:html_encode(ReasonPhrase); message(500,_,ConfigDB) -> ServerAdmin=lookup(ConfigDB,server_admin,"unknown@unknown"), "The server encountered an internal error or " "misconfiguration and was unable to complete " "your request.<P>Please contact the server administrator " - ++ ServerAdmin ++ ", and inform them of the time the error occurred " + ++ http_util:html_encode(ServerAdmin) ++ ", and inform them of the time the error occurred " "and anything you might have done that may have caused the error."; message(501,{Method, RequestURI, HTTPVersion}, _ConfigDB) -> if is_atom(Method) -> - atom_to_list(Method)++ - " to "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported."; + http_util:html_encode(atom_to_list(Method))++ + " to "++ http_util:html_encode(RequestURI)++" ("++ http_util:html_encode(HTTPVersion)++") not supported."; is_list(Method) -> - Method++ - " to "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported." + http_util:html_encode(Method)++ + " to "++ http_util:html_encode(RequestURI)++" ("++ http_util:html_encode(HTTPVersion)++") not supported." end; message(503, String, _ConfigDB) -> - "This service in unavailable due to: "++String. + "This service in unavailable due to: "++ http_util:html_encode(String). maybe_encode(URI) -> - case lists:member($%, URI) of - true -> - URI; - false -> - http_uri:encode(URI) - end. + Decoded = try http_uri:decode(URI) of + N -> N + catch + error:_ -> URI + end, + http_uri:encode(Decoded). %%convert_rfc_date(Date)->{{YYYY,MM,DD},{HH,MIN,SEC}} diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index 929185a67a..b85c479693 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -452,6 +452,10 @@ handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) -> ?hdrt("handle_body - send chunk", [{timeout, Timeout}, {size, Size}]), httpd_response:send_chunk(ModData, Body, IsDisableChunkedSend), receive + {esi_data, Data} when is_binary(Data) -> + ?hdrt("handle_body - received binary data (esi)", []), + handle_body(Pid, ModData, Data, Timeout, Size + byte_size(Data), + IsDisableChunkedSend); {esi_data, Data} -> ?hdrt("handle_body - received data (esi)", []), handle_body(Pid, ModData, Data, Timeout, Size + length(Data), diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..df18ffa9e8 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,9 +18,34 @@ {"%VSN%", [ + {"5.5.2", + [ + {load_module, ftp, soft_purge, soft_purge, []}, + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_file, soft_purge, soft_purge, []}, + {load_module, httpd_log, soft_purge, soft_purge, []}, + {load_module, mod_esi, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} + ] + }, {"5.5.1", [ - {load_module, http_chunk, soft_purge, soft_purge, []} + {load_module, ftp, soft_purge, soft_purge, []}, + {load_module, http_chunk, soft_purge, soft_purge, []}, + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_file, soft_purge, soft_purge, []}, + {load_module, httpd_log, soft_purge, soft_purge, []}, + {load_module, mod_esi, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, + [httpc_request, http_chunk]} ] }, {"5.5", @@ -34,10 +59,35 @@ ] } ], - [ + [ + {"5.5.2", + [ + {load_module, ftp, soft_purge, soft_purge, []}, + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_file, soft_purge, soft_purge, []}, + {load_module, httpd_log, soft_purge, soft_purge, []}, + {load_module, mod_esi, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} + ] + }, {"5.5.1", [ - {load_module, http_chunk, soft_purge, soft_purge, []} + {load_module, ftp, soft_purge, soft_purge, []}, + {load_module, http_chunk, soft_purge, soft_purge, []}, + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_file, soft_purge, soft_purge, []}, + {load_module, httpd_log, soft_purge, soft_purge, []}, + {load_module, mod_esi, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, + [httpc_request, http_chunk]} ] }, {"5.5", |