Skip to content
Newer
Older
100644 614 lines (556 sloc) 22.6 KB
cb7826c First version of ibrowse
chandrusf authored
1 %%%-------------------------------------------------------------------
2 %%% File : ibrowse.erl
3 %%% Author : Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
4 %%% Description : Load balancer process for HTTP client connections.
5 %%%
6 %%% Created : 11 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
7 %%%-------------------------------------------------------------------
9d7a637 Added host_header option
chandrusf authored
8 %% @author Chandrashekhar Mullaparthi <chandrashekhar dot mullaparthi at gmail dot com>
f752379 Lots of new features
chandrusf authored
9 %% @copyright 2005-2008 Chandrashekhar Mullaparthi
10 %% @version 1.4
cb7826c First version of ibrowse
chandrusf authored
11 %% @doc The ibrowse application implements an HTTP 1.1 client. This
12 %% module implements the API of the HTTP client. There is one named
f752379 Lots of new features
chandrusf authored
13 %% process called 'ibrowse' which assists in load balancing and maintaining configuration. There is one load balancing process per unique webserver. There is
cb7826c First version of ibrowse
chandrusf authored
14 %% one process to handle one TCP connection to a webserver
15 %% (implemented in the module ibrowse_http_client). Multiple connections to a
16 %% webserver are setup based on the settings for each webserver. The
17 %% ibrowse process also determines which connection to pipeline a
18 %% certain request on. The functions to call are send_req/3,
19 %% send_req/4, send_req/5, send_req/6.
20 %%
21 %% <p>Here are a few sample invocations.</p>
22 %%
23 %% <code>
24 %% ibrowse:send_req("http://intranet/messenger/", [], get).
25 %% <br/><br/>
26 %%
27 %% ibrowse:send_req("http://www.google.com/", [], get, [],
28 %% [{proxy_user, "XXXXX"},
29 %% {proxy_password, "XXXXX"},
30 %% {proxy_host, "proxy"},
31 %% {proxy_port, 8080}], 1000).
32 %% <br/><br/>
33 %%
34 %%ibrowse:send_req("http://www.erlang.org/download/otp_src_R10B-3.tar.gz", [], get, [],
35 %% [{proxy_user, "XXXXX"},
36 %% {proxy_password, "XXXXX"},
37 %% {proxy_host, "proxy"},
38 %% {proxy_port, 8080},
39 %% {save_response_to_file, true}], 1000).
40 %% <br/><br/>
41 %%
42 %% ibrowse:send_req("http://www.erlang.org", [], head).
43 %%
44 %% <br/><br/>
45 %% ibrowse:send_req("http://www.sun.com", [], options).
46 %%
47 %% <br/><br/>
48 %% ibrowse:send_req("http://www.bbc.co.uk", [], trace).
49 %%
50 %% <br/><br/>
51 %% ibrowse:send_req("http://www.google.com", [], get, [],
52 %% [{stream_to, self()}]).
53 %% </code>
54 %%
55 %% <p>A driver exists which implements URL encoding in C, but the
56 %% speed achieved using only erlang has been good enough, so the
57 %% driver isn't actually used.</p>
58
59 -module(ibrowse).
f752379 Lots of new features
chandrusf authored
60 -vsn('$Id: ibrowse.erl,v 1.6 2008/03/27 01:35:50 chandrusf Exp $ ').
cb7826c First version of ibrowse
chandrusf authored
61
62 -behaviour(gen_server).
63 %%--------------------------------------------------------------------
64 %% Include files
65 %%--------------------------------------------------------------------
66
67 %%--------------------------------------------------------------------
68 %% External exports
69 -export([start_link/0, start/0, stop/0]).
70
71 %% gen_server callbacks
72 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
73 terminate/2, code_change/3]).
74
75 %% API interface
f752379 Lots of new features
chandrusf authored
76 -export([
77 rescan_config/0,
78 rescan_config/1,
79 get_config_value/1,
80 get_config_value/2,
81 spawn_worker_process/2,
82 spawn_link_worker_process/2,
83 stop_worker_process/1,
84 send_req/3,
cb7826c First version of ibrowse
chandrusf authored
85 send_req/4,
86 send_req/5,
87 send_req/6,
f752379 Lots of new features
chandrusf authored
88 send_req_direct/4,
89 send_req_direct/5,
90 send_req_direct/6,
91 send_req_direct/7,
92 set_max_sessions/3,
93 set_max_pipeline_size/3,
94 set_dest/3,
cb7826c First version of ibrowse
chandrusf authored
95 trace_on/0,
96 trace_off/0,
97 trace_on/2,
98 trace_off/2,
f752379 Lots of new features
chandrusf authored
99 show_dest_status/2
100 ]).
cb7826c First version of ibrowse
chandrusf authored
101
102 -ifdef(debug).
103 -compile(export_all).
104 -endif.
105
f752379 Lots of new features
chandrusf authored
106 -import(ibrowse_lib, [
107 parse_url/1,
108 printable_date/0,
109 get_value/2,
110 get_value/3,
111 do_trace/2
112 ]).
113
114 -record(state, {trace = false}).
cb7826c First version of ibrowse
chandrusf authored
115
116 -include("ibrowse.hrl").
117
118 -define(DEF_MAX_SESSIONS,10).
119 -define(DEF_MAX_PIPELINE_SIZE,10).
120
121 %%====================================================================
122 %% External functions
123 %%====================================================================
124 %%--------------------------------------------------------------------
125 %% Function: start_link/0
126 %% Description: Starts the server
127 %%--------------------------------------------------------------------
f752379 Lots of new features
chandrusf authored
128 %% @doc Starts the ibrowse process linked to the calling process. Usually invoked by the supervisor ibrowse_sup
129 %% @spec start_link() -> {ok, pid()}
cb7826c First version of ibrowse
chandrusf authored
130 start_link() ->
131 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
132
f752379 Lots of new features
chandrusf authored
133 %% @doc Starts the ibrowse process without linking. Useful when testing using the shell
cb7826c First version of ibrowse
chandrusf authored
134 start() ->
135 gen_server:start({local, ?MODULE}, ?MODULE, [], [{debug, []}]).
136
f752379 Lots of new features
chandrusf authored
137 %% @doc Stop the ibrowse process. Useful when testing using the shell.
cb7826c First version of ibrowse
chandrusf authored
138 stop() ->
139 catch gen_server:call(ibrowse, stop).
140
141 %% @doc This is the basic function to send a HTTP request.
142 %% The Status return value indicates the HTTP status code returned by the webserver
143 %% @spec send_req(Url::string(), Headers::headerList(), Method::method()) -> response()
144 %% headerList() = [{header(), value()}]
145 %% header() = atom() | string()
146 %% value() = term()
6675c44 Patch provided by Eric Merritt to support WebDAV requests
chandrusf authored
147 %% method() = get | post | head | options | put | delete | trace | mkcol | propfind | proppatch | lock | unlock | move | copy
cb7826c First version of ibrowse
chandrusf authored
148 %% Status = string()
149 %% ResponseHeaders = [respHeader()]
150 %% respHeader() = {headerName(), headerValue()}
151 %% headerName() = string()
152 %% headerValue() = string()
153 %% response() = {ok, Status, ResponseHeaders, ResponseBody} | {error, Reason}
0cfebc0 A mofidied version of a patch sent in by Ram Krishnan to enhance the …
chandrusf authored
154 %% ResponseBody = string() | {file, Filename}
cb7826c First version of ibrowse
chandrusf authored
155 %% Reason = term()
156 send_req(Url, Headers, Method) ->
157 send_req(Url, Headers, Method, [], []).
158
159 %% @doc Same as send_req/3.
f752379 Lots of new features
chandrusf authored
160 %% If a list is specified for the body it has to be a flat list. The body can also be a fun/0 or a fun/1. <br/>
161 %% If fun/0, the connection handling process will repeatdely call the fun until it returns an error or eof. <pre>Fun() = {ok, Data} | eof</pre><br/>
162 %% If fun/1, the connection handling process will repeatedly call the fun with the supplied state until it returns an error or eof. <pre>Fun(State) = {ok, Data} | {ok, Data, NewState} | eof</pre>
cb7826c First version of ibrowse
chandrusf authored
163 %% @spec send_req(Url, Headers, Method::method(), Body::body()) -> response()
f752379 Lots of new features
chandrusf authored
164 %% body() = [] | string() | binary() | fun_arity_0() | {fun_arity_1(), initial_state()}
165 %% initial_state() = term()
cb7826c First version of ibrowse
chandrusf authored
166 send_req(Url, Headers, Method, Body) ->
167 send_req(Url, Headers, Method, Body, []).
168
169 %% @doc Same as send_req/4.
170 %% For a description of SSL Options, look in the ssl manpage. If the
9d7a637 Added host_header option
chandrusf authored
171 %% HTTP Version to use is not specified, the default is 1.1.
172 %% <br/>
173 %% <p>The <code>host_header</code> is useful in the case where ibrowse is
174 %% connecting to a component such as <a
175 %% href="http://www.stunnel.org">stunnel</a> which then sets up a
176 %% secure connection to a webserver. In this case, the URL supplied to
177 %% ibrowse must have the stunnel host/port details, but that won't
178 %% make sense to the destination webserver. This option can then be
179 %% used to specify what should go in the <code>Host</code> header in
180 %% the request.</p>
0cfebc0 A mofidied version of a patch sent in by Ram Krishnan to enhance the …
chandrusf authored
181 %% <ul>
182 %% <li>When both the options <code>save_response_to_file</code> and <code>stream_to</code>
183 %% are specified, the former takes precedence.</li>
184 %%
185 %% <li>For the <code>save_response_to_file</code> option, the response body is saved to
186 %% file only if the status code is in the 200-299 range. If not, the response body is returned
187 %% as a string.</li>
188 %% <li>Whenever an error occurs in the processing of a request, ibrowse will return as much
189 %% information as it has, such as HTTP Status Code and HTTP Headers. When this happens, the response
190 %% is of the form <code>{error, {Reason, {stat_code, StatusCode}, HTTP_headers}}</code></li>
191 %% </ul>
cb7826c First version of ibrowse
chandrusf authored
192 %% @spec send_req(Url::string(), Headers::headerList(), Method::method(), Body::body(), Options::optionList()) -> response()
193 %% optionList() = [option()]
60d16ae Added ability to generate Chunked requests (see option transfer_encod…
chandrusf authored
194 %% option() = {max_sessions, integer()} |
cb7826c First version of ibrowse
chandrusf authored
195 %% {max_pipeline_size, integer()} |
196 %% {trace, boolean()} |
197 %% {is_ssl, boolean()} |
198 %% {ssl_options, [SSLOpt]} |
199 %% {pool_name, atom()} |
200 %% {proxy_host, string()} |
201 %% {proxy_port, integer()} |
202 %% {proxy_user, string()} |
203 %% {proxy_password, string()} |
204 %% {use_absolute_uri, boolean()} |
205 %% {basic_auth, {username(), password()}} |
206 %% {cookie, string()} |
207 %% {content_length, integer()} |
208 %% {content_type, string()} |
0cfebc0 A mofidied version of a patch sent in by Ram Krishnan to enhance the …
chandrusf authored
209 %% {save_response_to_file, srtf()} |
cb7826c First version of ibrowse
chandrusf authored
210 %% {stream_to, process()} |
60d16ae Added ability to generate Chunked requests (see option transfer_encod…
chandrusf authored
211 %% {http_vsn, {MajorVsn, MinorVsn}} |
9d7a637 Added host_header option
chandrusf authored
212 %% {host_header, string()} |
60d16ae Added ability to generate Chunked requests (see option transfer_encod…
chandrusf authored
213 %% {transfer_encoding, {chunked, ChunkSize}}
214 %%
cb7826c First version of ibrowse
chandrusf authored
215 %% process() = pid() | atom()
216 %% username() = string()
217 %% password() = string()
218 %% SSLOpt = term()
60d16ae Added ability to generate Chunked requests (see option transfer_encod…
chandrusf authored
219 %% ChunkSize = integer()
0cfebc0 A mofidied version of a patch sent in by Ram Krishnan to enhance the …
chandrusf authored
220 %% srtf() = boolean() | filename()
221 %% filename() = string()
222 %%
cb7826c First version of ibrowse
chandrusf authored
223 send_req(Url, Headers, Method, Body, Options) ->
224 send_req(Url, Headers, Method, Body, Options, 30000).
225
226 %% @doc Same as send_req/5.
227 %% All timeout values are in milliseconds.
228 %% @spec send_req(Url, Headers::headerList(), Method::method(), Body::body(), Options::optionList(), Timeout) -> response()
229 %% Timeout = integer() | infinity
230 send_req(Url, Headers, Method, Body, Options, Timeout) ->
f752379 Lots of new features
chandrusf authored
231 case catch parse_url(Url) of
232 #url{host = Host,
233 port = Port} = Parsed_url ->
234 Lb_pid = case ets:lookup(ibrowse_lb, {Host, Port}) of
235 [] ->
236 get_lb_pid(Parsed_url);
237 [#lb_pid{pid = Lb_pid_1}] ->
238 Lb_pid_1
239 end,
240 Max_sessions = get_max_sessions(Host, Port, Options),
241 Max_pipeline_size = get_max_pipeline_size(Host, Port, Options),
242 {SSLOptions, IsSSL} =
243 case get_value(is_ssl, Options, false) of
244 false -> {[], false};
245 true -> {get_value(ssl_options, Options), true}
cb7826c First version of ibrowse
chandrusf authored
246 end,
f752379 Lots of new features
chandrusf authored
247 case ibrowse_lb:spawn_connection(Lb_pid, Parsed_url,
248 Max_sessions,
249 Max_pipeline_size,
250 {SSLOptions, IsSSL}) of
251 {ok, Conn_Pid} ->
252 do_send_req(Conn_Pid, Parsed_url, Headers,
253 Method, Body, Options, Timeout);
254 Err ->
255 Err
256 end;
257 Err ->
258 {error, {url_parsing_failed, Err}}
259 end.
260
261 get_lb_pid(Url) ->
262 gen_server:call(?MODULE, {get_lb_pid, Url}).
263
264 get_max_sessions(Host, Port, Options) ->
265 get_value(max_sessions, Options,
266 get_config_value({max_sessions, Host, Port}, ?DEF_MAX_SESSIONS)).
267
268 get_max_pipeline_size(Host, Port, Options) ->
269 get_value(max_pipeline_size, Options,
270 get_config_value({max_pipeline_size, Host, Port}, ?DEF_MAX_PIPELINE_SIZE)).
271
272 %% @doc Deprecated. Use set_max_sessions/3 and set_max_pipeline_size/3
273 %% for achieving the same effect.
274 set_dest(Host, Port, [{max_sessions, Max} | T]) ->
275 set_max_sessions(Host, Port, Max),
276 set_dest(Host, Port, T);
277 set_dest(Host, Port, [{max_pipeline_size, Max} | T]) ->
278 set_max_pipeline_size(Host, Port, Max),
279 set_dest(Host, Port, T);
280 set_dest(Host, Port, [{trace, Bool} | T]) when Bool == true; Bool == false ->
281 ibrowse ! {trace, true, Host, Port},
282 set_dest(Host, Port, T);
283 set_dest(_Host, _Port, [H | _]) ->
284 exit({invalid_option, H});
285 set_dest(_, _, []) ->
286 ok.
287
288 %% @doc Set the maximum number of connections allowed to a specific Host:Port.
289 %% @spec set_max_sessions(Host::string(), Port::integer(), Max::integer()) -> ok
290 set_max_sessions(Host, Port, Max) when is_integer(Max), Max > 0 ->
291 gen_server:call(?MODULE, {set_config_value, {max_sessions, Host, Port}, Max}).
292
293 %% @doc Set the maximum pipeline size for each connection to a specific Host:Port.
294 %% @spec set_max_pipeline_size(Host::string(), Port::integer(), Max::integer()) -> ok
295 set_max_pipeline_size(Host, Port, Max) when is_integer(Max), Max > 0 ->
296 gen_server:call(?MODULE, {set_config_value, {max_pipeline_size, Host, Port}, Max}).
297
298 do_send_req(Conn_Pid, Parsed_url, Headers, Method, Body, Options, Timeout) ->
299 case catch ibrowse_http_client:send_req(Conn_Pid, Parsed_url,
300 Headers, Method, Body,
301 Options, Timeout) of
cb7826c First version of ibrowse
chandrusf authored
302 {'EXIT', {timeout, _}} ->
f752379 Lots of new features
chandrusf authored
303 {error, req_timedout};
304 {'EXIT', Reason} ->
305 {error, {'EXIT', Reason}};
306 Ret ->
307 Ret
cb7826c First version of ibrowse
chandrusf authored
308 end.
309
f752379 Lots of new features
chandrusf authored
310 %% @doc Creates a HTTP client process to the specified Host:Port which
311 %% is not part of the load balancing pool. This is useful in cases
312 %% where some requests to a webserver might take a long time whereas
313 %% some might take a very short time. To avoid getting these quick
314 %% requests stuck in the pipeline behind time consuming requests, use
315 %% this function to get a handle to a connection process. <br/>
316 %% <b>Note:</b> Calling this function only creates a worker process. No connection
317 %% is setup. The connection attempt is made only when the first
318 %% request is sent via any of the send_req_direct/4,5,6,7 functions.<br/>
319 %% <b>Note:</b> It is the responsibility of the calling process to control
320 %% pipeline size on such connections.
321 %%
322 %% @spec spawn_worker_process(Host::string(), Port::integer()) -> {ok, pid()}
323 spawn_worker_process(Host, Port) ->
324 ibrowse_http_client:start({Host, Port}).
325
326 %% @doc Same as spawn_worker_process/2 except the the calling process
327 %% is linked to the worker process which is spawned.
328 spawn_link_worker_process(Host, Port) ->
329 ibrowse_http_client:start_link({Host, Port}).
330
331 %% @doc Terminate a worker process spawned using
332 %% spawn_worker_process/2 or spawn_link_worker_process/2. Requests in
333 %% progress will get the error response <pre>{error, closing_on_request}</pre>
334 %% @spec stop_worker_process(Conn_pid::pid()) -> ok
335 stop_worker_process(Conn_pid) ->
336 ibrowse_http_client:stop(Conn_pid).
337
338 %% @doc Same as send_req/3 except that the first argument is the PID
339 %% returned by spawn_worker_process/2 or spawn_link_worker_process/2
340 send_req_direct(Conn_pid, Url, Headers, Method) ->
341 send_req_direct(Conn_pid, Url, Headers, Method, [], []).
342
343 %% @doc Same as send_req/4 except that the first argument is the PID
344 %% returned by spawn_worker_process/2 or spawn_link_worker_process/2
345 send_req_direct(Conn_pid, Url, Headers, Method, Body) ->
346 send_req_direct(Conn_pid, Url, Headers, Method, Body, []).
347
348 %% @doc Same as send_req/5 except that the first argument is the PID
349 %% returned by spawn_worker_process/2 or spawn_link_worker_process/2
350 send_req_direct(Conn_pid, Url, Headers, Method, Body, Options) ->
351 send_req_direct(Conn_pid, Url, Headers, Method, Body, Options, 30000).
352
353 %% @doc Same as send_req/6 except that the first argument is the PID
354 %% returned by spawn_worker_process/2 or spawn_link_worker_process/2
355 send_req_direct(Conn_pid, Url, Headers, Method, Body, Options, Timeout) ->
356 case catch parse_url(Url) of
357 #url{} = Parsed_url ->
358 case do_send_req(Conn_pid, Parsed_url, Headers, Method, Body, Options, Timeout) of
359 {error, {'EXIT', {noproc, _}}} ->
360 {error, worker_is_dead};
361 Ret ->
362 Ret
363 end;
364 Err ->
365 {error, {url_parsing_failed, Err}}
366 end.
367
cb7826c First version of ibrowse
chandrusf authored
368 %% @doc Turn tracing on for the ibrowse process
369 trace_on() ->
370 ibrowse ! {trace, true}.
371 %% @doc Turn tracing off for the ibrowse process
372 trace_off() ->
373 ibrowse ! {trace, false}.
374
375 %% @doc Turn tracing on for all connections to the specified HTTP
376 %% server. Host is whatever is specified as the domain name in the URL
377 %% @spec trace_on(Host, Port) -> term()
378 %% Host = string()
379 %% Port = integer()
380 trace_on(Host, Port) ->
381 ibrowse ! {trace, true, Host, Port}.
382
383 %% @doc Turn tracing OFF for all connections to the specified HTTP
384 %% server.
385 %% @spec trace_off(Host, Port) -> term()
386 trace_off(Host, Port) ->
387 ibrowse ! {trace, false, Host, Port}.
388
f752379 Lots of new features
chandrusf authored
389 %% @doc Shows some internal information about load balancing to a
390 %% specified Host:Port. Info about workers spawned using
391 %% spawn_worker_process/2 or spawn_link_worker_process/2 is not
392 %% included.
393 show_dest_status(Host, Port) ->
394 case ets:lookup(ibrowse_lb, {Host, Port}) of
395 [] ->
396 no_active_processes;
397 [#lb_pid{pid = Lb_pid}] ->
398 io:format("Load Balancer Pid : ~p~n", [Lb_pid]),
399 io:format("LB process msg q size : ~p~n", [(catch process_info(Lb_pid, message_queue_len))]),
400 case lists:dropwhile(
401 fun(Tid) ->
402 ets:info(Tid, owner) /= Lb_pid
403 end, ets:all()) of
404 [] ->
405 io:format("Couldn't locate ETS table for ~p~n", [Lb_pid]);
406 [Tid | _] ->
407 First = ets:first(Tid),
408 Last = ets:last(Tid),
409 Size = ets:info(Tid, size),
410 io:format("LB ETS table id : ~p~n", [Tid]),
411 io:format("Num Connections : ~p~n", [Size]),
412 case Size of
413 0 ->
414 ok;
415 _ ->
416 {First_p_sz, _} = First,
417 {Last_p_sz, _} = Last,
418 io:format("Smallest pipeline : ~1000.p~n", [First_p_sz]),
419 io:format("Largest pipeline : ~1000.p~n", [Last_p_sz])
420 end
421 end
422 end.
cb7826c First version of ibrowse
chandrusf authored
423
f752379 Lots of new features
chandrusf authored
424 %% @doc Clear current configuration for ibrowse and load from the file
425 %% ibrowse.conf in the IBROWSE_EBIN/../priv directory. Current
426 %% configuration is cleared only if the ibrowse.conf file is readable
427 %% using file:consult/1
428 rescan_config() ->
429 gen_server:call(?MODULE, rescan_config).
cb7826c First version of ibrowse
chandrusf authored
430
f752379 Lots of new features
chandrusf authored
431 %% Clear current configuration for ibrowse and load from the specified
432 %% file. Current configuration is cleared only if the specified
433 %% file is readable using file:consult/1
434 rescan_config(File) when is_list(File) ->
435 gen_server:call(?MODULE, {rescan_config, File}).
cb7826c First version of ibrowse
chandrusf authored
436
437 %%====================================================================
438 %% Server functions
439 %%====================================================================
440
441 %%--------------------------------------------------------------------
442 %% Function: init/1
443 %% Description: Initiates the server
444 %% Returns: {ok, State} |
445 %% {ok, State, Timeout} |
446 %% ignore |
447 %% {stop, Reason}
448 %%--------------------------------------------------------------------
449 init(_) ->
450 process_flag(trap_exit, true),
451 State = #state{},
452 put(my_trace_flag, State#state.trace),
f752379 Lots of new features
chandrusf authored
453 put(ibrowse_trace_token, "ibrowse"),
454 ets:new(ibrowse_lb, [named_table, public, {keypos, 2}]),
455 ets:new(ibrowse_conf, [named_table, protected, {keypos, 2}]),
456 import_config(),
457 {ok, #state{}}.
458
459 import_config() ->
cb7826c First version of ibrowse
chandrusf authored
460 case code:priv_dir(ibrowse) of
f752379 Lots of new features
chandrusf authored
461 {error, _} = Err ->
462 Err;
cb7826c First version of ibrowse
chandrusf authored
463 PrivDir ->
464 Filename = filename:join(PrivDir, "ibrowse.conf"),
f752379 Lots of new features
chandrusf authored
465 import_config(Filename)
cb7826c First version of ibrowse
chandrusf authored
466 end.
467
f752379 Lots of new features
chandrusf authored
468 import_config(Filename) ->
469 case file:consult(Filename) of
470 {ok, Terms} ->
471 ets:delete_all_objects(ibrowse_conf),
472 Fun = fun({dest, Host, Port, MaxSess, MaxPipe, Options})
473 when list(Host), integer(Port),
474 integer(MaxSess), MaxSess > 0,
475 integer(MaxPipe), MaxPipe > 0, list(Options) ->
476 I = [{{max_sessions, Host, Port}, MaxSess},
477 {{max_pipeline_size, Host, Port}, MaxPipe},
478 {{options, Host, Port}, Options}],
479 lists:foreach(
480 fun({X, Y}) ->
481 ets:insert(ibrowse_conf,
482 #ibrowse_conf{key = X,
483 value = Y})
484 end, I);
485 ({K, V}) ->
486 ets:insert(ibrowse_conf,
487 #ibrowse_conf{key = K,
488 value = V});
489 (X) ->
490 io:format("Skipping unrecognised term: ~p~n", [X])
491 end,
492 lists:foreach(Fun, Terms);
493 Err ->
494 Err
495 end.
496
497 %% @doc Internal export
498 get_config_value(Key) ->
499 [#ibrowse_conf{value = V}] = ets:lookup(ibrowse_conf, Key),
500 V.
501
502 %% @doc Internal export
503 get_config_value(Key, DefVal) ->
504 case ets:lookup(ibrowse_conf, Key) of
505 [] ->
506 DefVal;
507 [#ibrowse_conf{value = V}] ->
508 V
509 end.
510
511 set_config_value(Key, Val) ->
512 ets:insert(ibrowse_conf, #ibrowse_conf{key = Key, value = Val}).
cb7826c First version of ibrowse
chandrusf authored
513 %%--------------------------------------------------------------------
514 %% Function: handle_call/3
515 %% Description: Handling call messages
516 %% Returns: {reply, Reply, State} |
517 %% {reply, Reply, State, Timeout} |
518 %% {noreply, State} |
519 %% {noreply, State, Timeout} |
520 %% {stop, Reason, Reply, State} | (terminate/2 is called)
521 %% {stop, Reason, State} (terminate/2 is called)
522 %%--------------------------------------------------------------------
f752379 Lots of new features
chandrusf authored
523 handle_call({get_lb_pid, #url{host = Host, port = Port} = Url}, _From, State) ->
524 Pid = do_get_connection(Url, ets:lookup(ibrowse_lb, {Host, Port})),
525 {reply, Pid, State};
cb7826c First version of ibrowse
chandrusf authored
526
f752379 Lots of new features
chandrusf authored
527 handle_call(stop, _From, State) ->
528 do_trace("IBROWSE shutting down~n", []),
529 {stop, normal, ok, State};
cb7826c First version of ibrowse
chandrusf authored
530
f752379 Lots of new features
chandrusf authored
531 handle_call({set_config_value, Key, Val}, _From, State) ->
532 set_config_value(Key, Val),
533 {reply, ok, State};
cb7826c First version of ibrowse
chandrusf authored
534
f752379 Lots of new features
chandrusf authored
535 handle_call(rescan_config, _From, State) ->
536 Ret = (catch import_config()),
537 {reply, Ret, State};
cb7826c First version of ibrowse
chandrusf authored
538
f752379 Lots of new features
chandrusf authored
539 handle_call({rescan_config, File}, _From, State) ->
540 Ret = (catch import_config(File)),
541 {reply, Ret, State};
542
543 handle_call(Request, _From, State) ->
544 Reply = {unknown_request, Request},
cb7826c First version of ibrowse
chandrusf authored
545 {reply, Reply, State}.
546
547 %%--------------------------------------------------------------------
548 %% Function: handle_cast/2
549 %% Description: Handling cast messages
550 %% Returns: {noreply, State} |
551 %% {noreply, State, Timeout} |
552 %% {stop, Reason, State} (terminate/2 is called)
553 %%--------------------------------------------------------------------
554
555 handle_cast(_Msg, State) ->
556 {noreply, State}.
557
558 %%--------------------------------------------------------------------
559 %% Function: handle_info/2
560 %% Description: Handling all non call/cast messages
561 %% Returns: {noreply, State} |
562 %% {noreply, State, Timeout} |
563 %% {stop, Reason, State} (terminate/2 is called)
564 %%--------------------------------------------------------------------
565 handle_info({trace, Bool}, State) ->
566 put(my_trace_flag, Bool),
f752379 Lots of new features
chandrusf authored
567 {noreply, State};
cb7826c First version of ibrowse
chandrusf authored
568
f752379 Lots of new features
chandrusf authored
569 handle_info({trace, Bool, Host, Port}, State) ->
570 Fun = fun(#lb_pid{host_port = {H, P}, pid = Pid}, _)
571 when H == Host,
572 P == Port ->
573 catch Pid ! {trace, Bool};
574 (#client_conn{key = {H, P, Pid}}, _)
575 when H == Host,
576 P == Port ->
577 catch Pid ! {trace, Bool};
578 (_, Acc) ->
579 Acc
580 end,
581 ets:foldl(Fun, undefined, ibrowse_lb),
582 ets:insert(ibrowse_conf, #ibrowse_conf{key = {trace, Host, Port},
583 value = Bool}),
584 {noreply, State};
585
cb7826c First version of ibrowse
chandrusf authored
586 handle_info(_Info, State) ->
587 {noreply, State}.
588
589 %%--------------------------------------------------------------------
590 %% Function: terminate/2
591 %% Description: Shutdown the server
592 %% Returns: any (ignored by gen_server)
593 %%--------------------------------------------------------------------
594 terminate(_Reason, _State) ->
595 ok.
596
597 %%--------------------------------------------------------------------
598 %% Func: code_change/3
599 %% Purpose: Convert process state when code is changed
600 %% Returns: {ok, NewState}
601 %%--------------------------------------------------------------------
602 code_change(_OldVsn, State, _Extra) ->
603 {ok, State}.
604
605 %%--------------------------------------------------------------------
606 %%% Internal functions
607 %%--------------------------------------------------------------------
f752379 Lots of new features
chandrusf authored
608 do_get_connection(#url{host = Host, port = Port}, []) ->
609 {ok, Pid} = ibrowse_lb:start_link([Host, Port]),
610 ets:insert(ibrowse_lb, #lb_pid{host_port = {Host, Port}, pid = Pid}),
611 Pid;
612 do_get_connection(_Url, [#lb_pid{pid = Pid}]) ->
613 Pid.
Something went wrong with that request. Please try again.