Browse Source

Added support for tunnelling through a proxy. Other minor changes as indicated in the README

pull/16/head
Chandrashekhar Mullaparthi 15 years ago
parent
commit
8a4653d4d4
11 changed files with 1325 additions and 1050 deletions
  1. +16
    -3
      README
  2. +47
    -29
      doc/ibrowse.html
  3. +1
    -1
      ebin/ibrowse.app
  4. +246
    -234
      src/ibrowse.erl
  5. +0
    -1
      src/ibrowse_app.erl
  6. +894
    -683
      src/ibrowse_http_client.erl
  7. +0
    -2
      src/ibrowse_lb.erl
  8. +120
    -93
      src/ibrowse_lib.erl
  9. +0
    -2
      src/ibrowse_sup.erl
  10. +0
    -1
      src/ibrowse_test.erl
  11. +1
    -1
      vsn.mk

+ 16
- 3
README View File

@ -18,12 +18,25 @@ ibrowse is available under two different licenses. LGPL and the BSD license.
Comments to : Chandrashekhar.Mullaparthi@gmail.com Comments to : Chandrashekhar.Mullaparthi@gmail.com
Version : 1.5.6
Version : 1.5.7
Latest version : git://github.com/cmullaparthi/ibrowse.git Latest version : git://github.com/cmullaparthi/ibrowse.git
CONTRIBUTIONS & CHANGE HISTORY CONTRIBUTIONS & CHANGE HISTORY
============================== ==============================
12-05-2010 - * Added support for the CONNECT method to tunnel HTTPS through
a proxy. When a https URL is requested through a proxy,
ibrowse will automatically use the CONNECT method to first
setup a tunnel through the proxy. Once this succeeds, the
actual request is dispatched. Successfully tested with the
new SSL implementation in R13B-03
* Added SSL support for direct connections.
See ibrowse:spawn_worker_process/1 and
ibrowse:spawn_link_worker_process/1
* Added option to return raw status line and raw unparsed headers
23-04-2010 - * Fixes to URL parsing by Karol Skocik
08-11-2009 - * Added option headers_as_is 08-11-2009 - * Added option headers_as_is
04-10-2009 - * Patch from Kostis Sagonas to cleanup some code and suppress 04-10-2009 - * Patch from Kostis Sagonas to cleanup some code and suppress
@ -132,7 +145,7 @@ CONTRIBUTIONS & CHANGE HISTORY
12-01-2007 - Derek Upham sent in a bug fix. The reset_state function was not 12-01-2007 - Derek Upham sent in a bug fix. The reset_state function was not
behaving correctly when the transfer encoding was not chunked. behaving correctly when the transfer encoding was not chunked.
13-11-2006 - Youn�s Hafri reported a bug where ibrowse was not returning the
13-11-2006 - Youn�s Hafri reported a bug where ibrowse was not returning the
temporary filename when the server was closing the connection temporary filename when the server was closing the connection
after sending the data (as in HTTP/1.0). after sending the data (as in HTTP/1.0).
Released ibrowse under the BSD license Released ibrowse under the BSD license
@ -151,7 +164,7 @@ CONTRIBUTIONS & CHANGE HISTORY
22-Nov-2005 - Added ability to generate requests using the Chunked 22-Nov-2005 - Added ability to generate requests using the Chunked
Transfer-Encoding. Transfer-Encoding.
08-May-2005 - Youn�s Hafri made a CRUX LINUX port of ibrowse.
08-May-2005 - Youn�s Hafri made a CRUX LINUX port of ibrowse.
http://yhafri.club.fr/crux/index.html http://yhafri.club.fr/crux/index.html
Here are some usage examples. Enjoy! Here are some usage examples. Enjoy!

+ 47
- 29
doc/ibrowse.html View File

@ -10,9 +10,9 @@
<h1>Module ibrowse</h1> <h1>Module ibrowse</h1>
<ul class="index"><li><a href="#description">Description</a></li><li><a href="#index">Function Index</a></li><li><a href="#functions">Function Details</a></li></ul>The ibrowse application implements an HTTP 1.1 client. <ul class="index"><li><a href="#description">Description</a></li><li><a href="#index">Function Index</a></li><li><a href="#functions">Function Details</a></li></ul>The ibrowse application implements an HTTP 1.1 client.
<p>Copyright © 2005-2009 Chandrashekhar Mullaparthi</p>
<p>Copyright © 2005-2010 Chandrashekhar Mullaparthi</p>
<p><b>Version:</b> 1.5.2</p>
<p><b>Version:</b> 1.6.0</p>
<p><b>Behaviours:</b> <a href="gen_server.html"><tt>gen_server</tt></a>.</p> <p><b>Behaviours:</b> <a href="gen_server.html"><tt>gen_server</tt></a>.</p>
<p><b>Authors:</b> Chandrashekhar Mullaparthi (<a href="mailto:chandrashekhar dot mullaparthi at gmail dot com"><tt>chandrashekhar dot mullaparthi at gmail dot com</tt></a>).</p> <p><b>Authors:</b> Chandrashekhar Mullaparthi (<a href="mailto:chandrashekhar dot mullaparthi at gmail dot com"><tt>chandrashekhar dot mullaparthi at gmail dot com</tt></a>).</p>
@ -28,23 +28,23 @@ send_req/4, send_req/5, send_req/6.

<p>Here are a few sample invocations.</p> <p>Here are a few sample invocations.</p>
<p><code>
<code>
ibrowse:send_req("http://intranet/messenger/", [], get). ibrowse:send_req("http://intranet/messenger/", [], get).
<br><br> <br><br>
ibrowse:send_req("http://www.google.com/", [], get, [], ibrowse:send_req("http://www.google.com/", [], get, [],
[{proxy_user, "XXXXX"},
{proxy_password, "XXXXX"},
{proxy_host, "proxy"},
{proxy_port, 8080}], 1000).
[{proxy_user, "XXXXX"},
{proxy_password, "XXXXX"},
{proxy_host, "proxy"},
{proxy_port, 8080}], 1000).
<br><br> <br><br>
ibrowse:send_req("http://www.erlang.org/download/otp_src_R10B-3.tar.gz", [], get, [], ibrowse:send_req("http://www.erlang.org/download/otp_src_R10B-3.tar.gz", [], get, [],
[{proxy_user, "XXXXX"},
{proxy_password, "XXXXX"},
{proxy_host, "proxy"},
{proxy_port, 8080},
{save_response_to_file, true}], 1000).
[{proxy_user, "XXXXX"},
{proxy_password, "XXXXX"},
{proxy_host, "proxy"},
{proxy_port, 8080},
{save_response_to_file, true}], 1000).
<br><br> <br><br>
ibrowse:send_req("http://www.erlang.org", [], head). ibrowse:send_req("http://www.erlang.org", [], head).
@ -58,11 +58,8 @@ send_req/4, send_req/5, send_req/6.

<br><br> <br><br>
ibrowse:send_req("http://www.google.com", [], get, [], ibrowse:send_req("http://www.google.com", [], get, [],
[{stream_to, self()}]). [{stream_to, self()}]).
</code></p>
</code>
<p>A driver exists which implements URL encoding in C, but the
speed achieved using only erlang has been good enough, so the
driver isn't actually used.</p>
<h2><a name="index">Function Index</a></h2> <h2><a name="index">Function Index</a></h2>
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#all_trace_off-0">all_trace_off/0</a></td><td>Turn Off ALL tracing.</td></tr> <table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#all_trace_off-0">all_trace_off/0</a></td><td>Turn Off ALL tracing.</td></tr>
<tr><td valign="top"><a href="#code_change-3">code_change/3</a></td><td></td></tr> <tr><td valign="top"><a href="#code_change-3">code_change/3</a></td><td></td></tr>
@ -93,10 +90,12 @@ send_req/4, send_req/5, send_req/6.

<tr><td valign="top"><a href="#show_dest_status-0">show_dest_status/0</a></td><td></td></tr> <tr><td valign="top"><a href="#show_dest_status-0">show_dest_status/0</a></td><td></td></tr>
<tr><td valign="top"><a href="#show_dest_status-2">show_dest_status/2</a></td><td>Shows some internal information about load balancing to a <tr><td valign="top"><a href="#show_dest_status-2">show_dest_status/2</a></td><td>Shows some internal information about load balancing to a
specified Host:Port.</td></tr> specified Host:Port.</td></tr>
<tr><td valign="top"><a href="#spawn_link_worker_process-2">spawn_link_worker_process/2</a></td><td>Same as spawn_worker_process/2 except the the calling process
<tr><td valign="top"><a href="#spawn_link_worker_process-1">spawn_link_worker_process/1</a></td><td>Same as spawn_worker_process/1 except the the calling process
is linked to the worker process which is spawned.</td></tr> is linked to the worker process which is spawned.</td></tr>
<tr><td valign="top"><a href="#spawn_worker_process-2">spawn_worker_process/2</a></td><td>Creates a HTTP client process to the specified Host:Port which
<tr><td valign="top"><a href="#spawn_link_worker_process-2">spawn_link_worker_process/2</a></td><td></td></tr>
<tr><td valign="top"><a href="#spawn_worker_process-1">spawn_worker_process/1</a></td><td>Creates a HTTP client process to the specified Host:Port which
is not part of the load balancing pool.</td></tr> is not part of the load balancing pool.</td></tr>
<tr><td valign="top"><a href="#spawn_worker_process-2">spawn_worker_process/2</a></td><td></td></tr>
<tr><td valign="top"><a href="#start-0">start/0</a></td><td>Starts the ibrowse process without linking.</td></tr> <tr><td valign="top"><a href="#start-0">start/0</a></td><td>Starts the ibrowse process without linking.</td></tr>
<tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td>Starts the ibrowse process linked to the calling process.</td></tr> <tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td>Starts the ibrowse process linked to the calling process.</td></tr>
<tr><td valign="top"><a href="#stop-0">stop/0</a></td><td>Stop the ibrowse process.</td></tr> <tr><td valign="top"><a href="#stop-0">stop/0</a></td><td>Stop the ibrowse process.</td></tr>
@ -203,7 +202,7 @@ send_req/4, send_req/5, send_req/6.

<div class="spec"> <div class="spec">
<p><tt>send_req(Url::string(), Headers::<a href="#type-headerList">headerList()</a>, Method::<a href="#type-method">method()</a>, Body::<a href="#type-body">body()</a>, Options::<a href="#type-optionList">optionList()</a>) -&gt; <a href="#type-response">response()</a></tt> <p><tt>send_req(Url::string(), Headers::<a href="#type-headerList">headerList()</a>, Method::<a href="#type-method">method()</a>, Body::<a href="#type-body">body()</a>, Options::<a href="#type-optionList">optionList()</a>) -&gt; <a href="#type-response">response()</a></tt>
<ul class="definitions"><li><tt><a name="type-optionList">optionList()</a> = [<a href="#type-option">option()</a>]</tt></li> <ul class="definitions"><li><tt><a name="type-optionList">optionList()</a> = [<a href="#type-option">option()</a>]</tt></li>
<li><tt><a name="type-option">option()</a> = {max_sessions, integer()} | {response_format, <a href="#type-response_format">response_format()</a>} | {stream_chunk_size, integer()} | {max_pipeline_size, integer()} | {trace, <a href="#type-boolean">boolean()</a>} | {is_ssl, <a href="#type-boolean">boolean()</a>} | {ssl_options, [SSLOpt]} | {pool_name, atom()} | {proxy_host, string()} | {proxy_port, integer()} | {proxy_user, string()} | {proxy_password, string()} | {use_absolute_uri, <a href="#type-boolean">boolean()</a>} | {basic_auth, {<a href="#type-username">username()</a>, <a href="#type-password">password()</a>}} | {cookie, string()} | {content_length, integer()} | {content_type, string()} | {save_response_to_file, <a href="#type-srtf">srtf()</a>} | {stream_to, <a href="#type-stream_to">stream_to()</a>} | {http_vsn, {MajorVsn, MinorVsn}} | {host_header, string()} | {inactivity_timeout, integer()} | {connect_timeout, integer()} | {socket_options, Sock_opts} | {transfer_encoding, {chunked, ChunkSize}}</tt></li>
<li><tt><a name="type-option">option()</a> = {max_sessions, integer()} | {response_format, <a href="#type-response_format">response_format()</a>} | {stream_chunk_size, integer()} | {max_pipeline_size, integer()} | {trace, <a href="#type-boolean">boolean()</a>} | {is_ssl, <a href="#type-boolean">boolean()</a>} | {ssl_options, [SSLOpt]} | {pool_name, atom()} | {proxy_host, string()} | {proxy_port, integer()} | {proxy_user, string()} | {proxy_password, string()} | {use_absolute_uri, <a href="#type-boolean">boolean()</a>} | {basic_auth, {<a href="#type-username">username()</a>, <a href="#type-password">password()</a>}} | {cookie, string()} | {content_length, integer()} | {content_type, string()} | {save_response_to_file, <a href="#type-srtf">srtf()</a>} | {stream_to, <a href="#type-stream_to">stream_to()</a>} | {http_vsn, {MajorVsn, MinorVsn}} | {host_header, string()} | {inactivity_timeout, integer()} | {connect_timeout, integer()} | {socket_options, Sock_opts} | {transfer_encoding, {chunked, ChunkSize}} | {headers_as_is, <a href="#type-boolean">boolean()</a>} | {give_raw_headers, <a href="#type-boolean">boolean()</a>}</tt></li>
<li><tt><a name="type-stream_to">stream_to()</a> = <a href="#type-process">process()</a> | {<a href="#type-process">process()</a>, once}</tt></li> <li><tt><a name="type-stream_to">stream_to()</a> = <a href="#type-process">process()</a> | {<a href="#type-process">process()</a>, once}</tt></li>
<li><tt><a name="type-process">process()</a> = pid() | atom()</tt></li> <li><tt><a name="type-process">process()</a> = pid() | atom()</tt></li>
<li><tt><a name="type-username">username()</a> = string()</tt></li> <li><tt><a name="type-username">username()</a> = string()</tt></li>
@ -220,14 +219,14 @@ send_req/4, send_req/5, send_req/6.

For a description of SSL Options, look in the <a href="http://www.erlang.org/doc/apps/ssl/index.html">ssl</a> manpage. If the For a description of SSL Options, look in the <a href="http://www.erlang.org/doc/apps/ssl/index.html">ssl</a> manpage. If the
HTTP Version to use is not specified, the default is 1.1. HTTP Version to use is not specified, the default is 1.1.
<br> <br>
<p>The <code>host_header</code> option is useful in the case where ibrowse is
<ul>
<li>The <code>host_header</code> option is useful in the case where ibrowse is
connecting to a component such as <a href="http://www.stunnel.org">stunnel</a> which then sets up a connecting to a component such as <a href="http://www.stunnel.org">stunnel</a> which then sets up a
secure connection to a webserver. In this case, the URL supplied to secure connection to a webserver. In this case, the URL supplied to
ibrowse must have the stunnel host/port details, but that won't ibrowse must have the stunnel host/port details, but that won't
make sense to the destination webserver. This option can then be make sense to the destination webserver. This option can then be
used to specify what should go in the <code>Host</code> header in used to specify what should go in the <code>Host</code> header in
the request.</p>
<ul>
the request.</li>
<li>The <code>stream_to</code> option can be used to have the HTTP <li>The <code>stream_to</code> option can be used to have the HTTP
response streamed to a process as messages as data arrives on the response streamed to a process as messages as data arrives on the
socket. If the calling process wishes to control the rate at which socket. If the calling process wishes to control the rate at which
@ -272,11 +271,20 @@ send_req/4, send_req/5, send_req/6.

request to complete will be 1000 milliseconds minus the time taken request to complete will be 1000 milliseconds minus the time taken
for connection setup. for connection setup.
</li> </li>
</ul>
<li> The <code>socket_options</code> option can be used to set <li> The <code>socket_options</code> option can be used to set
specific options on the socket. The <code>{active, true | false | once}</code> specific options on the socket. The <code>{active, true | false | once}</code>
and <code>{packet_type, Packet_type}</code> will be filtered out by ibrowse. </li> and <code>{packet_type, Packet_type}</code> will be filtered out by ibrowse. </li>
<li> The <code>headers_as_is</code> option is to enable the caller
to send headers exactly as specified in the request without ibrowse
adding some of its own. Required for some picky servers apparently. </li>
<li>The <code>give_raw_headers</code> option is to enable the
caller to get access to the raw status line and raw unparsed
headers. Not quite sure why someone would want this, but one of my
users asked for it, so here it is. </li>
</ul>
</p> </p>
<h3 class="function"><a name="send_req-6">send_req/6</a></h3> <h3 class="function"><a name="send_req-6">send_req/6</a></h3>
@ -340,15 +348,20 @@ send_req/4, send_req/5, send_req/6.

spawn_worker_process/2 or spawn_link_worker_process/2 is not spawn_worker_process/2 or spawn_link_worker_process/2 is not
included.</p> included.</p>
<h3 class="function"><a name="spawn_link_worker_process-2">spawn_link_worker_process/2</a></h3>
<h3 class="function"><a name="spawn_link_worker_process-1">spawn_link_worker_process/1</a></h3>
<div class="spec"> <div class="spec">
<p><tt>spawn_link_worker_process(Host, Port) -&gt; any()</tt></p>
</div><p>Same as spawn_worker_process/2 except the the calling process
<p><tt>spawn_link_worker_process(Url::string()) -&gt; {ok, pid()}</tt></p>
</div><p>Same as spawn_worker_process/1 except the the calling process
is linked to the worker process which is spawned.</p> is linked to the worker process which is spawned.</p>
<h3 class="function"><a name="spawn_worker_process-2">spawn_worker_process/2</a></h3>
<h3 class="function"><a name="spawn_link_worker_process-2">spawn_link_worker_process/2</a></h3>
<div class="spec"> <div class="spec">
<p><tt>spawn_worker_process(Host::string(), Port::integer()) -&gt; {ok, pid()}</tt></p>
<p><tt>spawn_link_worker_process(Host::string(), Port::integer()) -&gt; {ok, pid()}</tt></p>
</div>
<h3 class="function"><a name="spawn_worker_process-1">spawn_worker_process/1</a></h3>
<div class="spec">
<p><tt>spawn_worker_process(Url::string()) -&gt; {ok, pid()}</tt></p>
</div><p>Creates a HTTP client process to the specified Host:Port which </div><p>Creates a HTTP client process to the specified Host:Port which
is not part of the load balancing pool. This is useful in cases is not part of the load balancing pool. This is useful in cases
where some requests to a webserver might take a long time whereas where some requests to a webserver might take a long time whereas
@ -362,6 +375,11 @@ send_req/4, send_req/5, send_req/6.

pipeline size on such connections. pipeline size on such connections.
</p> </p>
<h3 class="function"><a name="spawn_worker_process-2">spawn_worker_process/2</a></h3>
<div class="spec">
<p><tt>spawn_worker_process(Host::string(), Port::integer()) -&gt; {ok, pid()}</tt></p>
</div>
<h3 class="function"><a name="start-0">start/0</a></h3> <h3 class="function"><a name="start-0">start/0</a></h3>
<div class="spec"> <div class="spec">
<p><tt>start() -&gt; any()</tt></p> <p><tt>start() -&gt; any()</tt></p>
@ -423,6 +441,6 @@ send_req/4, send_req/5, send_req/6.

<hr> <hr>
<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div> <div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
<p><i>Generated by EDoc, Sep 5 2009, 23:59:48.</i></p>
<p><i>Generated by EDoc, May 17 2010, 23:21:42.</i></p>
</body> </body>
</html> </html>

+ 1
- 1
ebin/ibrowse.app View File

@ -1,6 +1,6 @@
{application, ibrowse, {application, ibrowse,
[{description, "HTTP client application"}, [{description, "HTTP client application"},
{vsn, "1.5.3"},
{vsn, "1.6.0"},
{modules, [ ibrowse, {modules, [ ibrowse,
ibrowse_http_client, ibrowse_http_client,
ibrowse_app, ibrowse_app,

+ 246
- 234
src/ibrowse.erl View File

@ -6,8 +6,8 @@
%%% Created : 11 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk> %%% Created : 11 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%% @author Chandrashekhar Mullaparthi <chandrashekhar dot mullaparthi at gmail dot com> %% @author Chandrashekhar Mullaparthi <chandrashekhar dot mullaparthi at gmail dot com>
%% @copyright 2005-2009 Chandrashekhar Mullaparthi
%% @version 1.5.4
%% @copyright 2005-2010 Chandrashekhar Mullaparthi
%% @version 1.6.0
%% @doc The ibrowse application implements an HTTP 1.1 client. This %% @doc The ibrowse application implements an HTTP 1.1 client. This
%% module implements the API of the HTTP client. There is one named %% module implements the API of the HTTP client. There is one named
%% process called 'ibrowse' which assists in load balancing and maintaining configuration. There is one load balancing process per unique webserver. There is %% process called 'ibrowse' which assists in load balancing and maintaining configuration. There is one load balancing process per unique webserver. There is
@ -25,18 +25,18 @@
%% <br/><br/> %% <br/><br/>
%% %%
%% ibrowse:send_req("http://www.google.com/", [], get, [], %% ibrowse:send_req("http://www.google.com/", [], get, [],
%% [{proxy_user, "XXXXX"},
%% {proxy_password, "XXXXX"},
%% {proxy_host, "proxy"},
%% {proxy_port, 8080}], 1000).
%% [{proxy_user, "XXXXX"},
%% {proxy_password, "XXXXX"},
%% {proxy_host, "proxy"},
%% {proxy_port, 8080}], 1000).
%% <br/><br/> %% <br/><br/>
%% %%
%%ibrowse:send_req("http://www.erlang.org/download/otp_src_R10B-3.tar.gz", [], get, [], %%ibrowse:send_req("http://www.erlang.org/download/otp_src_R10B-3.tar.gz", [], get, [],
%% [{proxy_user, "XXXXX"},
%% {proxy_password, "XXXXX"},
%% {proxy_host, "proxy"},
%% {proxy_port, 8080},
%% {save_response_to_file, true}], 1000).
%% [{proxy_user, "XXXXX"},
%% {proxy_password, "XXXXX"},
%% {proxy_host, "proxy"},
%% {proxy_port, 8080},
%% {save_response_to_file, true}], 1000).
%% <br/><br/> %% <br/><br/>
%% %%
%% ibrowse:send_req("http://www.erlang.org", [], head). %% ibrowse:send_req("http://www.erlang.org", [], head).
@ -52,13 +52,8 @@
%% [{stream_to, self()}]). %% [{stream_to, self()}]).
%% </code> %% </code>
%% %%
%% <p>A driver exists which implements URL encoding in C, but the
%% speed achieved using only erlang has been good enough, so the
%% driver isn't actually used.</p>
-module(ibrowse). -module(ibrowse).
-vsn('$Id: ibrowse.erl,v 1.8 2009/07/01 22:43:19 chandrusf Exp $ ').
-behaviour(gen_server). -behaviour(gen_server).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Include files %% Include files
@ -70,48 +65,50 @@
%% gen_server callbacks %% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
terminate/2, code_change/3]).
%% API interface %% API interface
-export([ -export([
rescan_config/0,
rescan_config/1,
get_config_value/1,
get_config_value/2,
spawn_worker_process/2,
spawn_link_worker_process/2,
stop_worker_process/1,
send_req/3,
send_req/4,
send_req/5,
send_req/6,
send_req_direct/4,
send_req_direct/5,
send_req_direct/6,
send_req_direct/7,
stream_next/1,
set_max_sessions/3,
set_max_pipeline_size/3,
set_dest/3,
trace_on/0,
trace_off/0,
trace_on/2,
trace_off/2,
all_trace_off/0,
show_dest_status/0,
show_dest_status/2
]).
rescan_config/0,
rescan_config/1,
get_config_value/1,
get_config_value/2,
spawn_worker_process/1,
spawn_worker_process/2,
spawn_link_worker_process/1,
spawn_link_worker_process/2,
stop_worker_process/1,
send_req/3,
send_req/4,
send_req/5,
send_req/6,
send_req_direct/4,
send_req_direct/5,
send_req_direct/6,
send_req_direct/7,
stream_next/1,
set_max_sessions/3,
set_max_pipeline_size/3,
set_dest/3,
trace_on/0,
trace_off/0,
trace_on/2,
trace_off/2,
all_trace_off/0,
show_dest_status/0,
show_dest_status/2
]).
-ifdef(debug). -ifdef(debug).
-compile(export_all). -compile(export_all).
-endif. -endif.
-import(ibrowse_lib, [ -import(ibrowse_lib, [
parse_url/1,
get_value/3,
do_trace/2
]).
parse_url/1,
get_value/3,
do_trace/2
]).
-record(state, {trace = false}). -record(state, {trace = false}).
-include("ibrowse.hrl"). -include("ibrowse.hrl").
@ -173,15 +170,15 @@ send_req(Url, Headers, Method, Body) ->
%% For a description of SSL Options, look in the <a href="http://www.erlang.org/doc/apps/ssl/index.html">ssl</a> manpage. If the %% For a description of SSL Options, look in the <a href="http://www.erlang.org/doc/apps/ssl/index.html">ssl</a> manpage. If the
%% HTTP Version to use is not specified, the default is 1.1. %% HTTP Version to use is not specified, the default is 1.1.
%% <br/> %% <br/>
%% <p>The <code>host_header</code> option is useful in the case where ibrowse is
%% <ul>
%% <li>The <code>host_header</code> option is useful in the case where ibrowse is
%% connecting to a component such as <a %% connecting to a component such as <a
%% href="http://www.stunnel.org">stunnel</a> which then sets up a %% href="http://www.stunnel.org">stunnel</a> which then sets up a
%% secure connection to a webserver. In this case, the URL supplied to %% secure connection to a webserver. In this case, the URL supplied to
%% ibrowse must have the stunnel host/port details, but that won't %% ibrowse must have the stunnel host/port details, but that won't
%% make sense to the destination webserver. This option can then be %% make sense to the destination webserver. This option can then be
%% used to specify what should go in the <code>Host</code> header in %% used to specify what should go in the <code>Host</code> header in
%% the request.</p>
%% <ul>
%% the request.</li>
%% <li>The <code>stream_to</code> option can be used to have the HTTP %% <li>The <code>stream_to</code> option can be used to have the HTTP
%% response streamed to a process as messages as data arrives on the %% response streamed to a process as messages as data arrives on the
%% socket. If the calling process wishes to control the rate at which %% socket. If the calling process wishes to control the rate at which
@ -234,6 +231,11 @@ send_req(Url, Headers, Method, Body) ->
%% <li> The <code>headers_as_is</code> option is to enable the caller %% <li> The <code>headers_as_is</code> option is to enable the caller
%% to send headers exactly as specified in the request without ibrowse %% to send headers exactly as specified in the request without ibrowse
%% adding some of its own. Required for some picky servers apparently. </li> %% adding some of its own. Required for some picky servers apparently. </li>
%%
%% <li>The <code>give_raw_headers</code> option is to enable the
%% caller to get access to the raw status line and raw unparsed
%% headers. Not quite sure why someone would want this, but one of my
%% users asked for it, so here it is. </li>
%% </ul> %% </ul>
%% %%
%% @spec send_req(Url::string(), Headers::headerList(), Method::method(), Body::body(), Options::optionList()) -> response() %% @spec send_req(Url::string(), Headers::headerList(), Method::method(), Body::body(), Options::optionList()) -> response()
@ -263,7 +265,8 @@ send_req(Url, Headers, Method, Body) ->
%% {connect_timeout, integer()} | %% {connect_timeout, integer()} |
%% {socket_options, Sock_opts} | %% {socket_options, Sock_opts} |
%% {transfer_encoding, {chunked, ChunkSize}} | %% {transfer_encoding, {chunked, ChunkSize}} |
%% {headers_as_is, boolean()}
%% {headers_as_is, boolean()} |
%% {give_raw_headers, boolean()}
%% %%
%% stream_to() = process() | {process(), once} %% stream_to() = process() | {process(), once}
%% process() = pid() | atom() %% process() = pid() | atom()
@ -285,48 +288,48 @@ send_req(Url, Headers, Method, Body, Options) ->
%% Timeout = integer() | infinity %% Timeout = integer() | infinity
send_req(Url, Headers, Method, Body, Options, Timeout) -> send_req(Url, Headers, Method, Body, Options, Timeout) ->
case catch parse_url(Url) of case catch parse_url(Url) of
#url{host = Host,
port = Port,
protocol = Protocol} = Parsed_url ->
Lb_pid = case ets:lookup(ibrowse_lb, {Host, Port}) of
[] ->
get_lb_pid(Parsed_url);
[#lb_pid{pid = Lb_pid_1}] ->
Lb_pid_1
end,
Max_sessions = get_max_sessions(Host, Port, Options),
Max_pipeline_size = get_max_pipeline_size(Host, Port, Options),
Options_1 = merge_options(Host, Port, Options),
{SSLOptions, IsSSL} =
case (Protocol == https) orelse
get_value(is_ssl, Options_1, false) of
false -> {[], false};
true -> {get_value(ssl_options, Options_1, []), true}
end,
case ibrowse_lb:spawn_connection(Lb_pid, Parsed_url,
Max_sessions,
Max_pipeline_size,
{SSLOptions, IsSSL}) of
{ok, Conn_Pid} ->
do_send_req(Conn_Pid, Parsed_url, Headers,
Method, Body, Options_1, Timeout);
Err ->
Err
end;
Err ->
{error, {url_parsing_failed, Err}}
#url{host = Host,
port = Port,
protocol = Protocol} = Parsed_url ->
Lb_pid = case ets:lookup(ibrowse_lb, {Host, Port}) of
[] ->
get_lb_pid(Parsed_url);
[#lb_pid{pid = Lb_pid_1}] ->
Lb_pid_1
end,
Max_sessions = get_max_sessions(Host, Port, Options),
Max_pipeline_size = get_max_pipeline_size(Host, Port, Options),
Options_1 = merge_options(Host, Port, Options),
{SSLOptions, IsSSL} =
case (Protocol == https) orelse
get_value(is_ssl, Options_1, false) of
false -> {[], false};
true -> {get_value(ssl_options, Options_1, []), true}
end,
case ibrowse_lb:spawn_connection(Lb_pid, Parsed_url,
Max_sessions,
Max_pipeline_size,
{SSLOptions, IsSSL}) of
{ok, Conn_Pid} ->
do_send_req(Conn_Pid, Parsed_url, Headers,
Method, Body, Options_1, Timeout);
Err ->
Err
end;
Err ->
{error, {url_parsing_failed, Err}}
end. end.
merge_options(Host, Port, Options) -> merge_options(Host, Port, Options) ->
Config_options = get_config_value({options, Host, Port}, []), Config_options = get_config_value({options, Host, Port}, []),
lists:foldl( lists:foldl(
fun({Key, Val}, Acc) -> fun({Key, Val}, Acc) ->
case lists:keysearch(Key, 1, Options) of
false ->
[{Key, Val} | Acc];
_ ->
Acc
end
case lists:keysearch(Key, 1, Options) of
false ->
[{Key, Val} | Acc];
_ ->
Acc
end
end, Options, Config_options). end, Options, Config_options).
get_lb_pid(Url) -> get_lb_pid(Url) ->
@ -334,11 +337,11 @@ get_lb_pid(Url) ->
get_max_sessions(Host, Port, Options) -> get_max_sessions(Host, Port, Options) ->
get_value(max_sessions, Options, get_value(max_sessions, Options,
get_config_value({max_sessions, Host, Port}, ?DEF_MAX_SESSIONS)).
get_config_value({max_sessions, Host, Port}, ?DEF_MAX_SESSIONS)).
get_max_pipeline_size(Host, Port, Options) -> get_max_pipeline_size(Host, Port, Options) ->
get_value(max_pipeline_size, Options, get_value(max_pipeline_size, Options,
get_config_value({max_pipeline_size, Host, Port}, ?DEF_MAX_PIPELINE_SIZE)).
get_config_value({max_pipeline_size, Host, Port}, ?DEF_MAX_PIPELINE_SIZE)).
%% @doc Deprecated. Use set_max_sessions/3 and set_max_pipeline_size/3 %% @doc Deprecated. Use set_max_sessions/3 and set_max_pipeline_size/3
%% for achieving the same effect. %% for achieving the same effect.
@ -368,21 +371,21 @@ set_max_pipeline_size(Host, Port, Max) when is_integer(Max), Max > 0 ->
do_send_req(Conn_Pid, Parsed_url, Headers, Method, Body, Options, Timeout) -> do_send_req(Conn_Pid, Parsed_url, Headers, Method, Body, Options, Timeout) ->
case catch ibrowse_http_client:send_req(Conn_Pid, Parsed_url, case catch ibrowse_http_client:send_req(Conn_Pid, Parsed_url,
Headers, Method, ensure_bin(Body),
Options, Timeout) of
{'EXIT', {timeout, _}} ->
{error, req_timedout};
{'EXIT', Reason} ->
{error, {'EXIT', Reason}};
{ok, St_code, Headers, Body} = Ret when is_binary(Body) ->
case get_value(response_format, Options, list) of
list ->
{ok, St_code, Headers, binary_to_list(Body)};
binary ->
Ret
end;
Ret ->
Ret
Headers, Method, ensure_bin(Body),
Options, Timeout) of
{'EXIT', {timeout, _}} ->
{error, req_timedout};
{'EXIT', Reason} ->
{error, {'EXIT', Reason}};
{ok, St_code, Headers, Body} = Ret when is_binary(Body) ->
case get_value(response_format, Options, list) of
list ->
{ok, St_code, Headers, binary_to_list(Body)};
binary ->
Ret
end;
Ret ->
Ret
end. end.
ensure_bin(L) when is_list(L) -> list_to_binary(L); ensure_bin(L) when is_list(L) -> list_to_binary(L);
@ -403,12 +406,21 @@ ensure_bin({Fun, _} = Body) when is_function(Fun) -> Body.
%% <b>Note:</b> It is the responsibility of the calling process to control %% <b>Note:</b> It is the responsibility of the calling process to control
%% pipeline size on such connections. %% pipeline size on such connections.
%% %%
%% @spec spawn_worker_process(Url::string()) -> {ok, pid()}
spawn_worker_process(Url) ->
ibrowse_http_client:start(Url).
%% @spec spawn_worker_process(Host::string(), Port::integer()) -> {ok, pid()} %% @spec spawn_worker_process(Host::string(), Port::integer()) -> {ok, pid()}
spawn_worker_process(Host, Port) -> spawn_worker_process(Host, Port) ->
ibrowse_http_client:start({Host, Port}). ibrowse_http_client:start({Host, Port}).
%% @doc Same as spawn_worker_process/2 except the the calling process
%% @doc Same as spawn_worker_process/1 except the the calling process
%% is linked to the worker process which is spawned. %% is linked to the worker process which is spawned.
%% @spec spawn_link_worker_process(Url::string()) -> {ok, pid()}
spawn_link_worker_process(Url) ->
ibrowse_http_client:start_link(Url).
%% @spec spawn_link_worker_process(Host::string(), Port::integer()) -> {ok, pid()}
spawn_link_worker_process(Host, Port) -> spawn_link_worker_process(Host, Port) ->
ibrowse_http_client:start_link({Host, Port}). ibrowse_http_client:start_link({Host, Port}).
@ -438,17 +450,17 @@ send_req_direct(Conn_pid, Url, Headers, Method, Body, Options) ->
%% returned by spawn_worker_process/2 or spawn_link_worker_process/2 %% returned by spawn_worker_process/2 or spawn_link_worker_process/2
send_req_direct(Conn_pid, Url, Headers, Method, Body, Options, Timeout) -> send_req_direct(Conn_pid, Url, Headers, Method, Body, Options, Timeout) ->
case catch parse_url(Url) of case catch parse_url(Url) of
#url{host = Host,
port = Port} = Parsed_url ->
Options_1 = merge_options(Host, Port, Options),
case do_send_req(Conn_pid, Parsed_url, Headers, Method, Body, Options_1, Timeout) of
{error, {'EXIT', {noproc, _}}} ->
{error, worker_is_dead};
Ret ->
Ret
end;
Err ->
{error, {url_parsing_failed, Err}}
#url{host = Host,
port = Port} = Parsed_url ->
Options_1 = merge_options(Host, Port, Options),
case do_send_req(Conn_pid, Parsed_url, Headers, Method, Body, Options_1, Timeout) of
{error, {'EXIT', {noproc, _}}} ->
{error, worker_is_dead};
Ret ->
Ret
end;
Err ->
{error, {url_parsing_failed, Err}}
end. end.
%% @doc Tell ibrowse to stream the next chunk of data to the %% @doc Tell ibrowse to stream the next chunk of data to the
@ -457,11 +469,11 @@ send_req_direct(Conn_pid, Url, Headers, Method, Body, Options, Timeout) ->
%% @spec stream_next(Req_id :: req_id()) -> ok | {error, unknown_req_id} %% @spec stream_next(Req_id :: req_id()) -> ok | {error, unknown_req_id}
stream_next(Req_id) -> stream_next(Req_id) ->
case ets:lookup(ibrowse_stream, {req_id_pid, Req_id}) of case ets:lookup(ibrowse_stream, {req_id_pid, Req_id}) of
[] ->
{error, unknown_req_id};
[{_, Pid}] ->
catch Pid ! {stream_next, Req_id},
ok
[] ->
{error, unknown_req_id};
[{_, Pid}] ->
catch Pid ! {stream_next, Req_id},
ok
end. end.
%% @doc Turn tracing on for the ibrowse process %% @doc Turn tracing on for the ibrowse process
@ -495,75 +507,75 @@ all_trace_off() ->
show_dest_status() -> show_dest_status() ->
Dests = lists:filter(fun({lb_pid, {Host, Port}, _}) when is_list(Host), Dests = lists:filter(fun({lb_pid, {Host, Port}, _}) when is_list(Host),
is_integer(Port) ->
true;
(_) ->
false
end, ets:tab2list(ibrowse_lb)),
is_integer(Port) ->
true;
(_) ->
false
end, ets:tab2list(ibrowse_lb)),
All_ets = ets:all(), All_ets = ets:all(),
io:format("~-40.40s | ~-5.5s | ~-10.10s | ~s~n", io:format("~-40.40s | ~-5.5s | ~-10.10s | ~s~n",
["Server:port", "ETS", "Num conns", "LB Pid"]),
["Server:port", "ETS", "Num conns", "LB Pid"]),
io:format("~80.80.=s~n", [""]), io:format("~80.80.=s~n", [""]),
lists:foreach(fun({lb_pid, {Host, Port}, Lb_pid}) -> lists:foreach(fun({lb_pid, {Host, Port}, Lb_pid}) ->
case lists:dropwhile(
fun(Tid) ->
ets:info(Tid, owner) /= Lb_pid
end, All_ets) of
[] ->
io:format("~40.40s | ~-5.5s | ~-5.5s | ~s~n",
[Host ++ ":" ++ integer_to_list(Port),
"",
"",
io_lib:format("~p", [Lb_pid])]
);
[Tid | _] ->
catch (
begin
Size = ets:info(Tid, size),
io:format("~40.40s | ~-5.5s | ~-5.5s | ~s~n",
[Host ++ ":" ++ integer_to_list(Port),
integer_to_list(Tid),
integer_to_list(Size),
io_lib:format("~p", [Lb_pid])]
)
end
)
end
end, Dests).
case lists:dropwhile(
fun(Tid) ->
ets:info(Tid, owner) /= Lb_pid
end, All_ets) of
[] ->
io:format("~40.40s | ~-5.5s | ~-5.5s | ~s~n",
[Host ++ ":" ++ integer_to_list(Port),
"",
"",
io_lib:format("~p", [Lb_pid])]
);
[Tid | _] ->
catch (
begin
Size = ets:info(Tid, size),
io:format("~40.40s | ~-5.5s | ~-5.5s | ~s~n",
[Host ++ ":" ++ integer_to_list(Port),
integer_to_list(Tid),
integer_to_list(Size),
io_lib:format("~p", [Lb_pid])]
)
end
)
end
end, Dests).
%% @doc Shows some internal information about load balancing to a %% @doc Shows some internal information about load balancing to a
%% specified Host:Port. Info about workers spawned using %% specified Host:Port. Info about workers spawned using
%% spawn_worker_process/2 or spawn_link_worker_process/2 is not %% spawn_worker_process/2 or spawn_link_worker_process/2 is not
%% included. %% included.
show_dest_status(Host, Port) -> show_dest_status(Host, Port) ->
case ets:lookup(ibrowse_lb, {Host, Port}) of case ets:lookup(ibrowse_lb, {Host, Port}) of
[] ->
no_active_processes;
[#lb_pid{pid = Lb_pid}] ->
io:format("Load Balancer Pid : ~p~n", [Lb_pid]),
io:format("LB process msg q size : ~p~n", [(catch process_info(Lb_pid, message_queue_len))]),
case lists:dropwhile(
fun(Tid) ->
ets:info(Tid, owner) /= Lb_pid
end, ets:all()) of
[] ->
io:format("Couldn't locate ETS table for ~p~n", [Lb_pid]);
[Tid | _] ->
First = ets:first(Tid),
Last = ets:last(Tid),
Size = ets:info(Tid, size),
io:format("LB ETS table id : ~p~n", [Tid]),
io:format("Num Connections : ~p~n", [Size]),
case Size of
0 ->
ok;
_ ->
{First_p_sz, _} = First,
{Last_p_sz, _} = Last,
io:format("Smallest pipeline : ~1000.p~n", [First_p_sz]),
io:format("Largest pipeline : ~1000.p~n", [Last_p_sz])
end
end
[] ->
no_active_processes;
[#lb_pid{pid = Lb_pid}] ->
io:format("Load Balancer Pid : ~p~n", [Lb_pid]),
io:format("LB process msg q size : ~p~n", [(catch process_info(Lb_pid, message_queue_len))]),
case lists:dropwhile(
fun(Tid) ->
ets:info(Tid, owner) /= Lb_pid
end, ets:all()) of
[] ->
io:format("Couldn't locate ETS table for ~p~n", [Lb_pid]);
[Tid | _] ->
First = ets:first(Tid),
Last = ets:last(Tid),
Size = ets:info(Tid, size),
io:format("LB ETS table id : ~p~n", [Tid]),
io:format("Num Connections : ~p~n", [Size]),
case Size of
0 ->
ok;
_ ->
{First_p_sz, _} = First,
{Last_p_sz, _} = Last,
io:format("Smallest pipeline : ~1000.p~n", [First_p_sz]),
io:format("Largest pipeline : ~1000.p~n", [Last_p_sz])
end
end
end. end.
%% @doc Clear current configuration for ibrowse and load from the file %% @doc Clear current configuration for ibrowse and load from the file
@ -604,40 +616,40 @@ init(_) ->
import_config() -> import_config() ->
case code:priv_dir(ibrowse) of case code:priv_dir(ibrowse) of
{error, _} = Err ->
Err;
PrivDir ->
Filename = filename:join(PrivDir, "ibrowse.conf"),
import_config(Filename)
{error, _} = Err ->
Err;
PrivDir ->
Filename = filename:join(PrivDir, "ibrowse.conf"),
import_config(Filename)
end. end.
import_config(Filename) -> import_config(Filename) ->
case file:consult(Filename) of case file:consult(Filename) of
{ok, Terms} ->
ets:delete_all_objects(ibrowse_conf),
Fun = fun({dest, Host, Port, MaxSess, MaxPipe, Options})
when is_list(Host), is_integer(Port),
is_integer(MaxSess), MaxSess > 0,
is_integer(MaxPipe), MaxPipe > 0, is_list(Options) ->
I = [{{max_sessions, Host, Port}, MaxSess},
{{max_pipeline_size, Host, Port}, MaxPipe},
{{options, Host, Port}, Options}],
lists:foreach(
fun({X, Y}) ->
ets:insert(ibrowse_conf,
#ibrowse_conf{key = X,
value = Y})
end, I);
({K, V}) ->
ets:insert(ibrowse_conf,
#ibrowse_conf{key = K,
value = V});
(X) ->
io:format("Skipping unrecognised term: ~p~n", [X])
end,
lists:foreach(Fun, Terms);
Err ->
Err
{ok, Terms} ->
ets:delete_all_objects(ibrowse_conf),
Fun = fun({dest, Host, Port, MaxSess, MaxPipe, Options})
when is_list(Host), is_integer(Port),
is_integer(MaxSess), MaxSess > 0,
is_integer(MaxPipe), MaxPipe > 0, is_list(Options) ->
I = [{{max_sessions, Host, Port}, MaxSess},
{{max_pipeline_size, Host, Port}, MaxPipe},
{{options, Host, Port}, Options}],
lists:foreach(
fun({X, Y}) ->
ets:insert(ibrowse_conf,
#ibrowse_conf{key = X,
value = Y})
end, I);
({K, V}) ->
ets:insert(ibrowse_conf,
#ibrowse_conf{key = K,
value = V});
(X) ->
io:format("Skipping unrecognised term: ~p~n", [X])
end,
lists:foreach(Fun, Terms);
Err ->
Err
end. end.
%% @doc Internal export %% @doc Internal export
@ -648,10 +660,10 @@ get_config_value(Key) ->
%% @doc Internal export %% @doc Internal export
get_config_value(Key, DefVal) -> get_config_value(Key, DefVal) ->
case ets:lookup(ibrowse_conf, Key) of case ets:lookup(ibrowse_conf, Key) of
[] ->
DefVal;
[#ibrowse_conf{value = V}] ->
V
[] ->
DefVal;
[#ibrowse_conf{value = V}] ->
V
end. end.
set_config_value(Key, Val) -> set_config_value(Key, Val) ->
@ -712,36 +724,36 @@ handle_info(all_trace_off, State) ->
Mspec = [{{ibrowse_conf,{trace,'$1','$2'},true},[],[{{'$1','$2'}}]}], Mspec = [{{ibrowse_conf,{trace,'$1','$2'},true},[],[{{'$1','$2'}}]}],
Trace_on_dests = ets:select(ibrowse_conf, Mspec), Trace_on_dests = ets:select(ibrowse_conf, Mspec),
Fun = fun(#lb_pid{host_port = {H, P}, pid = Pid}, _) -> Fun = fun(#lb_pid{host_port = {H, P}, pid = Pid}, _) ->
case lists:member({H, P}, Trace_on_dests) of
false ->
ok;
true ->
catch Pid ! {trace, false}
end;
(_, Acc) ->
Acc
end,
case lists:member({H, P}, Trace_on_dests) of
false ->
ok;
true ->
catch Pid ! {trace, false}
end;
(_, Acc) ->
Acc
end,
ets:foldl(Fun, undefined, ibrowse_lb), ets:foldl(Fun, undefined, ibrowse_lb),
ets:select_delete(ibrowse_conf, [{{ibrowse_conf,{trace,'$1','$2'},true},[],['true']}]), ets:select_delete(ibrowse_conf, [{{ibrowse_conf,{trace,'$1','$2'},true},[],['true']}]),
{noreply, State}; {noreply, State};
handle_info({trace, Bool}, State) -> handle_info({trace, Bool}, State) ->
put(my_trace_flag, Bool), put(my_trace_flag, Bool),
{noreply, State}; {noreply, State};
handle_info({trace, Bool, Host, Port}, State) -> handle_info({trace, Bool, Host, Port}, State) ->
Fun = fun(#lb_pid{host_port = {H, P}, pid = Pid}, _) Fun = fun(#lb_pid{host_port = {H, P}, pid = Pid}, _)
when H == Host,
P == Port ->
catch Pid ! {trace, Bool};
(_, Acc) ->
Acc
end,
when H == Host,
P == Port ->
catch Pid ! {trace, Bool};
(_, Acc) ->
Acc
end,
ets:foldl(Fun, undefined, ibrowse_lb), ets:foldl(Fun, undefined, ibrowse_lb),
ets:insert(ibrowse_conf, #ibrowse_conf{key = {trace, Host, Port}, ets:insert(ibrowse_conf, #ibrowse_conf{key = {trace, Host, Port},
value = Bool}),
value = Bool}),
{noreply, State}; {noreply, State};
handle_info(_Info, State) -> handle_info(_Info, State) ->
{noreply, State}. {noreply, State}.

+ 0
- 1
src/ibrowse_app.erl View File

@ -6,7 +6,6 @@
%%% Created : 15 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk> %%% Created : 15 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(ibrowse_app). -module(ibrowse_app).
-vsn('$Id: ibrowse_app.erl,v 1.1 2005/05/05 22:28:28 chandrusf Exp $ ').
-behaviour(application). -behaviour(application).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------

+ 894
- 683
src/ibrowse_http_client.erl
File diff suppressed because it is too large
View File


+ 0
- 2
src/ibrowse_lb.erl View File

@ -6,8 +6,6 @@
%%% Created : 6 Mar 2008 by chandru <chandrashekhar.mullaparthi@t-mobile.co.uk> %%% Created : 6 Mar 2008 by chandru <chandrashekhar.mullaparthi@t-mobile.co.uk>
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(ibrowse_lb). -module(ibrowse_lb).
-vsn('$Id: ibrowse_lb.erl,v 1.2 2009/07/01 22:43:19 chandrusf Exp $ ').
-author(chandru). -author(chandru).
-behaviour(gen_server). -behaviour(gen_server).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------

+ 120
- 93
src/ibrowse_lib.erl View File

@ -5,7 +5,6 @@
%% @doc Module with a few useful functions %% @doc Module with a few useful functions
-module(ibrowse_lib). -module(ibrowse_lib).
-vsn('$Id: ibrowse_lib.erl,v 1.6 2008/03/27 01:35:50 chandrusf Exp $ ').
-author('chandru'). -author('chandru').
-ifdef(debug). -ifdef(debug).
-compile(export_all). -compile(export_all).
@ -14,22 +13,22 @@
-include("ibrowse.hrl"). -include("ibrowse.hrl").
-export([ -export([
get_trace_status/2,
do_trace/2,
do_trace/3,
url_encode/1,
decode_rfc822_date/1,
status_code/1,
dec2hex/2,
drv_ue/1,
drv_ue/2,
encode_base64/1,
decode_base64/1,
get_value/2,
get_value/3,
parse_url/1,
printable_date/0
]).
get_trace_status/2,
do_trace/2,
do_trace/3,
url_encode/1,
decode_rfc822_date/1,
status_code/1,
dec2hex/2,
drv_ue/1,
drv_ue/2,
encode_base64/1,
decode_base64/1,
get_value/2,
get_value/3,
parse_url/1,
printable_date/0
]).
get_trace_status(Host, Port) -> get_trace_status(Host, Port) ->
ibrowse:get_config_value({trace, Host, Port}, false). ibrowse:get_config_value({trace, Host, Port}, false).
@ -39,10 +38,10 @@ drv_ue(Str) ->
drv_ue(Str, Port). drv_ue(Str, Port).
drv_ue(Str, Port) -> drv_ue(Str, Port) ->
case erlang:port_control(Port, 1, Str) of case erlang:port_control(Port, 1, Str) of
[] ->
Str;
Res ->
Res
[] ->
Str;
Res ->
Res
end. end.
%% @doc URL-encodes a string based on RFC 1738. Returns a flat list. %% @doc URL-encodes a string based on RFC 1738. Returns a flat list.
@ -72,10 +71,10 @@ d2h(N) -> N+$a-10.
decode_rfc822_date(String) when is_list(String) -> decode_rfc822_date(String) when is_list(String) ->
case catch decode_rfc822_date_1(string:tokens(String, ", \t\r\n")) of case catch decode_rfc822_date_1(string:tokens(String, ", \t\r\n")) of
{'EXIT', _} ->
{error, invalid_date};
Res ->
Res
{'EXIT', _} ->
{error, invalid_date};
Res ->
Res
end. end.
% TODO: Have to handle the Zone % TODO: Have to handle the Zone
@ -86,15 +85,15 @@ decode_rfc822_date_1([Day,Month,Year, Time,_Zone]) ->
MonthI = month_int(Month), MonthI = month_int(Month),
YearI = list_to_integer(Year), YearI = list_to_integer(Year),
TimeTup = case string:tokens(Time, ":") of TimeTup = case string:tokens(Time, ":") of
[H,M] ->
{list_to_integer(H),
list_to_integer(M),
0};
[H,M,S] ->
{list_to_integer(H),
list_to_integer(M),
list_to_integer(S)}
end,
[H,M] ->
{list_to_integer(H),
list_to_integer(M),
0};
[H,M,S] ->
{list_to_integer(H),
list_to_integer(M),
list_to_integer(S)}
end,
{{YearI,MonthI,DayI}, TimeTup}. {{YearI,MonthI,DayI}, TimeTup}.
month_int("Jan") -> 1; month_int("Jan") -> 1;
@ -204,9 +203,9 @@ decode_base64(Bin) when is_binary(Bin) ->
list_to_binary(List). list_to_binary(List).
decode_base64_1([H | T], Acc) when ((H == $\t) or decode_base64_1([H | T], Acc) when ((H == $\t) or
(H == 32) or
(H == $\r) or
(H == $\n)) ->
(H == 32) or
(H == $\r) or
(H == $\n)) ->
decode_base64_1(T, Acc); decode_base64_1(T, Acc);
decode_base64_1([$=, $=], Acc) -> decode_base64_1([$=, $=], Acc) ->
@ -254,10 +253,10 @@ b64_to_int($/) -> 63.
get_value(Tag, TVL, DefVal) -> get_value(Tag, TVL, DefVal) ->
case lists:keysearch(Tag, 1, TVL) of case lists:keysearch(Tag, 1, TVL) of
false ->
DefVal;
{value, {_, Val}} ->
Val
false ->
DefVal;
{value, {_, Val}} ->
Val
end. end.
get_value(Tag, TVL) -> get_value(Tag, TVL) ->
@ -272,13 +271,20 @@ parse_url([$:, $/, $/ | _], get_protocol, Url, []) ->
parse_url([$:, $/, $/ | T], get_protocol, Url, TmpAcc) -> parse_url([$:, $/, $/ | T], get_protocol, Url, TmpAcc) ->
Prot = list_to_atom(lists:reverse(TmpAcc)), Prot = list_to_atom(lists:reverse(TmpAcc)),
parse_url(T, get_username, parse_url(T, get_username,
Url#url{protocol = Prot},
[]);
parse_url([$/ | T], get_username, Url, TmpAcc) ->
Url#url{protocol = Prot},
[]);
parse_url([H | T], get_username, Url, TmpAcc) when H == $/;
H == $? ->
Path = case H of
$/ ->
[$/ | T];
$? ->
[$/, $? | T]
end,
%% No username/password. No port number %% No username/password. No port number
Url#url{host = lists:reverse(TmpAcc), Url#url{host = lists:reverse(TmpAcc),
port = default_port(Url#url.protocol),
path = [$/ | T]};
port = default_port(Url#url.protocol),
path = Path};
parse_url([$: | T], get_username, Url, TmpAcc) -> parse_url([$: | T], get_username, Url, TmpAcc) ->
%% It is possible that no username/password has been %% It is possible that no username/password has been
%% specified. But we'll continue with the assumption that there is %% specified. But we'll continue with the assumption that there is
@ -286,77 +292,98 @@ parse_url([$: | T], get_username, Url, TmpAcc) ->
%% username/password indeed. If we encounter a '/', it was %% username/password indeed. If we encounter a '/', it was
%% actually the hostname %% actually the hostname
parse_url(T, get_password, parse_url(T, get_password,
Url#url{username = lists:reverse(TmpAcc)},
[]);
Url#url{username = lists:reverse(TmpAcc)},
[]);
parse_url([$@ | T], get_username, Url, TmpAcc) -> parse_url([$@ | T], get_username, Url, TmpAcc) ->
parse_url(T, get_host, parse_url(T, get_host,
Url#url{username = lists:reverse(TmpAcc),
password = ""},
[]);
Url#url{username = lists:reverse(TmpAcc),
password = ""},
[]);
parse_url([$@ | T], get_password, Url, TmpAcc) -> parse_url([$@ | T], get_password, Url, TmpAcc) ->
parse_url(T, get_host, parse_url(T, get_host,
Url#url{password = lists:reverse(TmpAcc)},
[]);
parse_url([$/ | T], get_password, Url, TmpAcc) ->
Url#url{password = lists:reverse(TmpAcc)},
[]);
parse_url([H | T], get_password, Url, TmpAcc) when H == $/;
H == $? ->
%% Ok, what we thought was the username/password was the hostname %% Ok, what we thought was the username/password was the hostname
%% and portnumber %% and portnumber
#url{username=User} = Url, #url{username=User} = Url,
Port = list_to_integer(lists:reverse(TmpAcc)), Port = list_to_integer(lists:reverse(TmpAcc)),
Path = case H of
$/ ->
[$/ | T];
$? ->
[$/, $? | T]
end,
Url#url{host = User, Url#url{host = User,
port = Port,
username = undefined,
password = undefined,
path = [$/ | T]};
port = Port,
username = undefined,
password = undefined,
path = Path};
parse_url([$: | T], get_host, #url{} = Url, TmpAcc) -> parse_url([$: | T], get_host, #url{} = Url, TmpAcc) ->
parse_url(T, get_port, parse_url(T, get_port,
Url#url{host = lists:reverse(TmpAcc)},
[]);
parse_url([$/ | T], get_host, #url{protocol=Prot} = Url, TmpAcc) ->
Url#url{host = lists:reverse(TmpAcc)},
[]);
parse_url([H | T], get_host, #url{protocol=Prot} = Url, TmpAcc) when H == $/;
H == $? ->
Path = case H of
$/ ->
[$/ | T];
$? ->
[$/, $? | T]
end,
Url#url{host = lists:reverse(TmpAcc), Url#url{host = lists:reverse(TmpAcc),
port = default_port(Prot),
path = [$/ | T]};
parse_url([$/ | T], get_port, #url{protocol=Prot} = Url, TmpAcc) ->
port = default_port(Prot),
path = Path};
parse_url([H | T], get_port, #url{protocol=Prot} = Url, TmpAcc) when H == $/;
H == $? ->
Path = case H of
$/ ->
[$/ | T];
$? ->
[$/, $? | T]
end,
Port = case TmpAcc of Port = case TmpAcc of
[] ->
default_port(Prot);
_ ->
list_to_integer(lists:reverse(TmpAcc))
end,
Url#url{port = Port, path = [$/ | T]};
[] ->
default_port(Prot);
_ ->
list_to_integer(lists:reverse(TmpAcc))
end,
Url#url{port = Port, path = Path};
parse_url([H | T], State, Url, TmpAcc) -> parse_url([H | T], State, Url, TmpAcc) ->
parse_url(T, State, Url, [H | TmpAcc]); parse_url(T, State, Url, [H | TmpAcc]);
parse_url([], get_host, Url, TmpAcc) when TmpAcc /= [] -> parse_url([], get_host, Url, TmpAcc) when TmpAcc /= [] ->
Url#url{host = lists:reverse(TmpAcc), Url#url{host = lists:reverse(TmpAcc),
port = default_port(Url#url.protocol),
path = "/"};
port = default_port(Url#url.protocol),
path = "/"};
parse_url([], get_username, Url, TmpAcc) when TmpAcc /= [] -> parse_url([], get_username, Url, TmpAcc) when TmpAcc /= [] ->
Url#url{host = lists:reverse(TmpAcc), Url#url{host = lists:reverse(TmpAcc),
port = default_port(Url#url.protocol),
path = "/"};
port = default_port(Url#url.protocol),
path = "/"};
parse_url([], get_port, #url{protocol=Prot} = Url, TmpAcc) -> parse_url([], get_port, #url{protocol=Prot} = Url, TmpAcc) ->
Port = case TmpAcc of Port = case TmpAcc of
[] ->
default_port(Prot);
_ ->
list_to_integer(lists:reverse(TmpAcc))
end,
[] ->
default_port(Prot);
_ ->
list_to_integer(lists:reverse(TmpAcc))
end,
Url#url{port = Port, Url#url{port = Port,
path = "/"};
path = "/"};
parse_url([], get_password, Url, TmpAcc) -> parse_url([], get_password, Url, TmpAcc) ->
%% Ok, what we thought was the username/password was the hostname %% Ok, what we thought was the username/password was the hostname
%% and portnumber %% and portnumber
#url{username=User} = Url, #url{username=User} = Url,
Port = case TmpAcc of Port = case TmpAcc of
[] ->
default_port(Url#url.protocol);
_ ->
list_to_integer(lists:reverse(TmpAcc))
end,
[] ->
default_port(Url#url.protocol);
_ ->
list_to_integer(lists:reverse(TmpAcc))
end,
Url#url{host = User, Url#url{host = User,
port = Port,
username = undefined,
password = undefined,
path = "/"};
port = Port,
username = undefined,
password = undefined,
path = "/"};
parse_url([], State, Url, TmpAcc) -> parse_url([], State, Url, TmpAcc) ->
{invalid_uri_2, State, Url, TmpAcc}. {invalid_uri_2, State, Url, TmpAcc}.
@ -387,13 +414,13 @@ do_trace(Fmt, Args) ->
-ifdef(DEBUG). -ifdef(DEBUG).
do_trace(_, Fmt, Args) -> do_trace(_, Fmt, Args) ->
io:format("~s -- (~s) - "++Fmt, io:format("~s -- (~s) - "++Fmt,
[printable_date(),
get(ibrowse_trace_token) | Args]).
[printable_date(),
get(ibrowse_trace_token) | Args]).
-else. -else.
do_trace(true, Fmt, Args) -> do_trace(true, Fmt, Args) ->
io:format("~s -- (~s) - "++Fmt, io:format("~s -- (~s) - "++Fmt,
[printable_date(),
get(ibrowse_trace_token) | Args]);
[printable_date(),
get(ibrowse_trace_token) | Args]);
do_trace(_, _, _) -> do_trace(_, _, _) ->
ok. ok.
-endif. -endif.

+ 0
- 2
src/ibrowse_sup.erl View File

@ -6,8 +6,6 @@
%%% Created : 15 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk> %%% Created : 15 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(ibrowse_sup). -module(ibrowse_sup).
-vsn('$Id: ibrowse_sup.erl,v 1.1 2005/05/05 22:28:28 chandrusf Exp $ ').
-behaviour(supervisor). -behaviour(supervisor).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Include files %% Include files

+ 0
- 1
src/ibrowse_test.erl View File

@ -4,7 +4,6 @@
%%% Created : 14 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk> %%% Created : 14 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
-module(ibrowse_test). -module(ibrowse_test).
-vsn('$Id: ibrowse_test.erl,v 1.4 2009/07/01 22:43:19 chandrusf Exp $ ').
-export([ -export([
load_test/3, load_test/3,
send_reqs_1/3, send_reqs_1/3,

+ 1
- 1
vsn.mk View File

@ -1,2 +1,2 @@
IBROWSE_VSN = 1.5.6
IBROWSE_VSN = 1.6.0

Loading…
Cancel
Save