Annotation of java/classes/org/w3c/www/protocol/http/HttpManager.java, revision 1.5
1.1 abaird 1: // HttpManager.java
1.5 ! abaird 2: // $Id: HttpManager.java,v 1.4 1996/09/03 02:48:51 abaird Exp $
1.1 abaird 3: // (c) COPYRIGHT MIT and INRIA, 1996.
4: // Please first read the full copyright statement in file COPYRIGHT.html
5:
6: package w3c.www.protocol.http ;
7:
8: import java.util.*;
9: import java.net.*;
10: import java.io.*; // FIXME - DEBUG
11:
12: import w3c.www.mime.*;
1.4 abaird 13: import w3c.util.*;
1.1 abaird 14:
15: class ReplyFactory implements MimeParserFactory {
16:
17: public MimeHeaderHolder createHeaderHolder(MimeParser parser) {
18: return new Reply(parser);
19: }
20:
21: }
22:
23:
24: /**
25: * The client side HTTP request manager.
26: * This class is the user interface (along with the other public classes of
27: * this package) for the W3C client side library implementing HTTP.
28: * A typicall request is launched though the following sequence:
29: * <pre>
30: * HttpManager manager = HttpManager.getManager() ;
31: * Request request = manager.makeRequest() ;
32: * request.setMethod(HTTP.GET) ;
33: * request.setURL(new URL("http://www.w3.org/pub/WWW/"));
34: * Reply reply = manager.runRequest(request) ;
35: * // Get the reply input stream that contains the actual data:
36: * InputStream in = reply.getInputStream() ;
37: * ...
38: * </pre>
39: */
40:
41: public class HttpManager {
1.4 abaird 42:
1.2 abaird 43: /**
44: * The name of the property containing the ProprequestFilter to launch.
45: */
1.4 abaird 46: public static final String FILTERS_PROP = "w3c.www.protocol.http.filters";
1.5 ! abaird 47: /**
! 48: * Header properties - The allowed drift for getting cached resources.
! 49: */
! 50: public static final
! 51: String MAX_STALE = "w3c.www.protocol.http.cache.maxStale";
! 52: /**
! 53: * Header properties - The minium freshness required on cached resources.
! 54: */
! 55: public static final
! 56: String MIN_FRESH = "w3c.www.protocol.http.cache.minFresh";
! 57: /**
! 58: * Header properties - Set the only if cached flag on requests.
! 59: */
! 60: public static final
! 61: String ONLY_IF_CACHED = "w3c.www.protocol.http.cache.onlyIfCached";
! 62: /**
! 63: * Header properties - Set the user agent.
! 64: */
! 65: public static final
! 66: String USER_AGENT = "w3c.www.protocol.http.userAgent";
! 67: /**
! 68: * Header properties - Set the accept header.
! 69: */
! 70: public static final
! 71: String ACCEPT = "w3c.www.protocol.http.accept";
! 72: /**
! 73: * Header properties - Set the accept language.
! 74: */
! 75: public static final
! 76: String ACCEPT_LANGUAGE = "w3c.www.protocol.http.acceptLanguage";
! 77: /**
! 78: * Header properties - Set the accept encodings.
! 79: */
! 80: public static final
! 81: String ACCEPT_ENCODING = "w3c.www.protocol.http.acceptEncoding";
! 82: /**
! 83: * Header properties - Should we use a proxy ?
! 84: */
! 85: public static final
! 86: String PROXY_SET = "XproxySet";
! 87: /**
! 88: * Header properties - What is the proxy host name.
! 89: */
! 90: public static final
! 91: String PROXY_HOST = "XproxyHost";
! 92: /**
! 93: * Header properties - What is the proxy port number.
! 94: */
! 95: public static final
! 96: String PROXY_PORT = "XproxyPort";
1.2 abaird 97:
1.1 abaird 98: private static HttpManager manager = null;
99:
100: /**
101: * The server this manager knows about, indexed by FQDN of target servers.
102: */
103: protected Hashtable servers = null;
104: /**
105: * The template request (the request we will clone to create new requests)
106: */
1.4 abaird 107: protected Request template = null ;
108: /**
109: * The LRU list of created servers.
110: */
111: protected LRUList serversLru = null;
112: /**
113: * The currrent number of opened connections.
114: */
115: protected int conn_count = 0;
116: /**
117: * The maximum number of allowed connections.
118: */
119: protected int conn_max = 32;
1.1 abaird 120: /**
121: * The filter engine attached to this manager.
122: */
123: FilterEngine filteng = null;
124:
125:
126: /**
1.4 abaird 127: * Allow the manager to interact with the user if needed.
128: * This will, for example, allow prompting for paswords, etc.
129: * @param onoff Turn interaction on or off.
130: */
131:
132: public void setAllowUserInteraction(boolean onoff) {
133: template.setAllowUserInteraction(onoff);
134: }
135:
136: /**
1.1 abaird 137: * Get an instance of the HTTP manager.
138: * This method returns an actual instance of the HTTP manager. It may
139: * return different managers, if it decides to distribute the load on
140: * different managers (avoid the HttpManager being a bottleneck).
141: * @return An application wide instance of the HTTP manager.
142: */
143:
144: public static synchronized HttpManager getManager() {
145: if ( manager == null ) {
146: manager = new HttpManager() ;
1.2 abaird 147: // Initialize this new manager:
148: String filters = System.getProperty(FILTERS_PROP);
149: if ( filters == null )
150: return manager;
151: StringTokenizer st = new StringTokenizer(filters, "|");
152: while (st.hasMoreTokens() ) {
153: String cls = (String) st.nextElement();
154: try {
155: Class c = Class.forName(cls);
156: PropRequestFilter f = (PropRequestFilter) c.newInstance();
157: f.initialize(manager);
158: } catch (Exception ex) {
159: System.err.println("Error initializing prop filters:");
160: System.err.println("Coulnd't initialize ["
161: + cls
162: + "]: " + ex.getMessage());
163: ex.printStackTrace();
164: }
165: }
1.5 ! abaird 166: Request tpl = manager.template;
! 167: // Set some default headers value (from props)
! 168: // Check for a proxy ?
! 169: String propval = System.getProperty(PROXY_SET);
! 170: if ( propval != null ) {
! 171: // Wow using a proxy now !
! 172: String host = System.getProperty(PROXY_HOST);
! 173: int port = -1;
! 174: URL proxy = null;
! 175: try {
! 176: propval = System.getProperty(PROXY_PORT);
! 177: port = Integer.parseInt(propval);
! 178: proxy = new URL("http", host, port, "/");
! 179: } catch (Exception ex) {
! 180: }
! 181: // Now if a proxy...
! 182: if ( proxy != null )
! 183: tpl.setProxy(proxy);
! 184: }
! 185: // CacheControl, only-if-cached
! 186: if (System.getProperty(ONLY_IF_CACHED) != null)
! 187: tpl.setOnlyIfCached(true);
! 188: // CacheControl, maxstale
! 189: propval = System.getProperty(MAX_STALE);
! 190: if ( propval != null ) {
! 191: try {
! 192: int maxstale = Integer.parseInt(propval);
! 193: tpl.setMaxStale(maxstale);
! 194: } catch (Exception ex) {
! 195: }
! 196: }
! 197: // CacheControl, minfresh:
! 198: propval = System.getProperty(MIN_FRESH);
! 199: if ( propval != null ) {
! 200: try {
! 201: int minfresh = Integer.parseInt(propval);
! 202: System.out.println("*** minfresh = "+minfresh);
! 203: tpl.setMinFresh(minfresh);
! 204: } catch (Exception ex) {
! 205: }
! 206: }
! 207: // General, User agent
! 208: propval = System.getProperty(USER_AGENT);
! 209: if ( propval != null )
! 210: tpl.setValue("user-agent", propval);
! 211: // General, Accept
! 212: propval = System.getProperty(ACCEPT);
! 213: if ( propval != null )
! 214: tpl.setValue("accept", propval);
! 215: // General, Accept-Language
! 216: propval = System.getProperty(ACCEPT_LANGUAGE);
! 217: if ( propval != null )
! 218: tpl.setValue("accept-language", propval);
! 219: // General, Accept-Encoding
! 220: propval = System.getProperty(ACCEPT_ENCODING);
! 221: if ( propval != null )
! 222: tpl.setValue("accept-encoding", propval);
1.1 abaird 223: }
224: return manager;
225: }
226:
227:
228: /**
229: * Get the appropriate server object for handling request to given target.
230: * @param key The server's identifier encoded as a <code>host:port</code>
231: * String.
232: * @return An object complying to the HttpServer interface.
233: * @exception HttpException If the given host name couldn't be resolved.
234: */
235:
1.5 ! abaird 236: protected synchronized HttpServer lookupServer(String host, int port)
1.1 abaird 237: throws HttpException
238: {
1.5 ! abaird 239: int p = (port == -1) ? 80 : port;
! 240: String id = (p == 80) ? host : (host +":"+p);
1.1 abaird 241: // Check for an existing server:
242: HttpServer server = (HttpServer) servers.get(id);
243: if ( server != null )
244: return server;
245: // Create and register a new server:
246: server = new HttpBasicServer();
1.5 ! abaird 247: server.initialize(this, host, p);
1.1 abaird 248: servers.put(id, server);
1.4 abaird 249: serversLru.toHead(server);
1.1 abaird 250: return server;
251: }
1.5 ! abaird 252:
1.1 abaird 253: /**
1.4 abaird 254: * The given server is about to be used.
255: * Update our list of available servers.
256: * @param server The server that is being used.
257: */
258:
259: public void notifyUse(HttpServer server) {
260: serversLru.toHead(server);
261: }
262:
263: /**
264: * Close all the connections that this server has established.
265: * This server has remain idle for some time, we can now try to close
266: * all its connections.
267: * @param server The server whose connections are to be closed.
268: * @return A boolean, <strong>true</strong> if we can establish more
269: * connections, <strong>false</strong> otherwise.
270: */
271:
272: public boolean closeServer(HttpServer server) {
273: server.closeConnections();
274: return (conn_count < conn_max);
275: }
276:
277: /**
278: * A new client connection has been established.
279: * This method will try to maintain a maximum number of established
280: * connections, by closing idle connections when possible.
281: * @param server The server that has established a new connection.
282: */
283:
284: public synchronized void incrConnCount(HttpServer server) {
285: System.out.println("incrConnCount: "+(conn_count+1));
286: if ( ++conn_count > conn_max ) {
287: // Do some clean-up:
288: HttpServer s = (HttpServer) serversLru.getTail();
289: while (s != null) {
290: if ((s != server) && closeServer(s))
291: break;
292: s = (HttpServer) s.getPrev();
293: }
294: }
295: return;
296: }
297:
298: /**
299: * Decrement the number of established connections.
300: * @param server The server that has closed one connection to its target.
301: */
302:
303: public synchronized void decrConnCount(HttpServer server) {
304: System.out.println("decrConnCount: "+(conn_count-1));
305: --conn_count;
306: }
307:
308: /**
1.1 abaird 309: * One of our server handler wants to open a connection.
310: * @param block A boolean indicating whether we should block the calling
311: * thread until a token is available (otherwise, the method will just
312: * peek at the connection count, and return the appropriate result).
313: * @return A boolean, <strong>true</strong> if the connection can be
314: * opened straight, <strong>false</strong> otherwise.
315: */
316:
1.4 abaird 317: protected boolean negotiateConnection(HttpServer server, int ccount) {
318: if ( ccount <= 1 ) {
319: // Always allow at least two connections per server (deadlocks)
320: return true;
321: } else if ( conn_count+1 < conn_max ) {
322: // We can use more connections:
323: return true;
324: }
325: // Try to close some other server:
326: if ( servers.size() > conn_max ) {
327: HttpServer last = (HttpServer) serversLru.getTail();
328: return (last != server) && closeServer(last);
329: } else {
330: return false;
331: }
1.1 abaird 332: }
333:
334: /**
335: * Run the given request, in synchronous mode.
336: * This method will launch the given request, and block the calling thread
337: * until the response headers are available.
338: * @param request The request to run.
339: * @return An instance of Reply, containing all the reply
340: * informations.
341: * @exception HTTPException If something failed during request processing.
342: */
343:
344: public Reply runRequest(Request request)
345: throws HttpException
346: {
347: URL target = request.getURL();
348: // Locate the appropriate target server:
1.5 ! abaird 349: HttpServer server = null;
! 350: URL proxy = request.getProxy();
! 351: if ( proxy != null ) {
! 352: server = lookupServer(proxy.getHost(), proxy.getPort());
! 353: } else {
! 354: server = lookupServer(target.getHost(), target.getPort());
! 355: }
1.1 abaird 356: // Now run through the ingoing filters:
357: RequestFilter filters[] = filteng.run(request);
358: if ( filters != null ) {
359: for (int i = 0 ; i < filters.length ; i++) {
1.3 abaird 360: Reply fr = filters[i].ingoingFilter(request);
361: if ( fr != null )
362: return fr;
1.1 abaird 363: }
364: }
365: // Get the server to give back a reply:
366: Reply reply = server.runRequest(request);
367: // Apply the filters on the way back:
368: if ( filters != null ) {
1.3 abaird 369: for (int i = 0 ; i < filters.length ; i++) {
370: Reply fr = filters[i].outgoingFilter(request, reply);
371: if ( fr != null )
372: return fr;
373: }
1.1 abaird 374: }
375: return reply;
376: }
377:
378: /**
379: * Get this manager's reply factory.
380: * The Reply factory is used when prsing incomming reply from servers, it
381: * decides what object will be created to hold the actual reply from the
382: * server.
383: * @return An object compatible with the MimeParserFactory interface.
384: */
385:
386: MimeParserFactory factory = null ;
387:
388: public synchronized MimeParserFactory getReplyFactory() {
389: if ( factory == null )
390: factory = new ReplyFactory();
391: return factory;
392: }
393:
394: /**
395: * Add a new request filter.
396: * Request filters are called <em>before</em> a request is launched, and
397: * <em>after</em> the reply headers are available. They allow applications
398: * to setup specific request headers (such as PICS, or PEP stuff) on the
399: * way in, and check the reply on the way out.
400: * <p>Request filters are application wide: if their scope matches
401: * the current request, then they will always be aplied.
402: * <p>Filter scopes are defined inclusively and exclusively
403: * @param filter The request filter to add.
404: */
405:
406: public void setFilter(URL incs[], URL exs[], RequestFilter filter) {
407: if ( incs != null ) {
408: for (int i = 0 ; i < incs.length ; i++)
409: filteng.setFilter(incs[i], true, filter);
410: }
411: if ( exs != null ) {
412: for (int i = 0 ; i < exs.length ; i++)
413: filteng.setFilter(exs[i], false, filter);
414: }
415: return;
416: }
417:
418: public void setFilter(RequestFilter filter) {
419: filteng.setFilter(filter);
420: }
421:
422: /**
423: * Add a request processor.
424: * Request processors are application wide hooks, able to answer request
425: * by querying a local cache. An application can set as many request
426: * processor as it wants, each of them will be called in trun (in the order
427: * they were registered), if any of them returns a reply, the request
428: * processing will be halted, and the generated reply returned.
429: * <p>Request processors can also be used to query distant cache, through
430: * some home-brew protocols.
431: * @param processor The request processor to be added.
432: */
433:
434: public void addProcessor(RequestProcessor processor) {
435: }
436:
437: /**
438: * Remove a request processor.
439: * Remove the given request processor.
440: * @return A boolean, <strong>true</strong> if the processor was found
441: * and removed, <strong>false</strong> otherwise.
442: */
443:
444: public boolean removeProcessor(RequestProcessor processor) {
445: return false;
446: }
447:
448: /**
449: * Create a new default outgoing request.
450: * This method should <em>always</em> be used to create outgoing requests.
451: * It will initialize the request with appropriate default values for
452: * the various headers, and make sure that the request is enhanced by
453: * the registered request filters.
454: * @return An instance of Request, suitable to be launched.
455: */
456:
457: public Request createRequest() {
458: return (Request) template.getClone() ;
459: }
460:
461: /**
462: * Global settings - Set the max number of allowed connections.
463: * Set the maximum number of simultaneous connections that can remain
464: * opened. The manager will take care of queuing requests if this number
465: * is reached.
466: * <p>This value defaults to the value of the
467: * <code>w3c.www.http.maxConnections</code> property.
468: * @param max_conn The allowed maximum simultaneous open connections.
469: */
470:
471: public void setMaxConnections(int max_conn) {
472: }
473:
474: /**
475: * Global settings - Set an optional proxy to use.
476: * Set the proxy to which all requests should be targeted. If the
477: * <code>w3c.www.http.proxy</code> property is defined, it will be
478: * used as the default value.
479: * @param proxy The URL for the proxy to use.
480: */
481:
482: public void setProxy(URL proxy) {
1.5 ! abaird 483: template.setProxy(proxy);
1.1 abaird 484: }
485:
486: /**
487: * Global settings - Set the request timeout.
488: * Once a request has been emited, the HttpManager will sit for this
489: * given number of milliseconds before the request is declared to have
490: * timed-out.
491: * <p>This timeout value defaults to the value of the
492: * <code>w3c.www.http.requestTimeout</code> property value.
493: * @param ms The timeout value in milliseconds.
494: */
495:
496: public void setRequestTimeout(int ms) {
497: }
498:
499: /**
500: * Global settings - Define a global request header.
501: * Set a default value for some request header. Once defined, the
502: * header will automatically be defined on <em>all</em> outgoing requests
503: * created through the <code>createRequest</code> request.
504: * @param name The name of the header, case insensitive.
505: * @param value It's default value.
506: */
507:
508: public void setGlobalHeader(String name, String value) {
509: template.setValue(name, value);
510: }
511:
512: public String getGlobalHeader(String name) {
513: return template.getValue(name);
514: }
515:
516: /**
517: * Create a new HttpManager.
518: * This can only be called from this package. The caller must rather
519: * use the <code>getManager</code> method.
520: * @param props The properties from which the manager should initialize
521: * itself, or <strong>null</strong> if none are available.
522: */
523:
524: HttpManager(Properties props) {
1.4 abaird 525: this.template = new Request(this);
526: this.servers = new Hashtable();
527: this.filteng = new FilterEngine();
528: this.serversLru = new SyncLRUList();
1.1 abaird 529: }
530:
531: HttpManager() {
532: this(System.getProperties());
533: }
534:
535: /**
536: * DEBUGGING !
537: */
538:
539: public static void main(String args[]) {
540: try {
541: // Get the manager, and define some global headers:
542: HttpManager manager = HttpManager.getManager();
543: manager.setGlobalHeader("User-Agent", "Jigsaw/1.0a");
544: manager.setGlobalHeader("Accept", "*/*;q=1.0");
545: manager.setGlobalHeader("Accept-Encoding", "gzip");
546: Request request = manager.createRequest();
547: request.setURL(new URL(args[0]));
548: request.setMethod("GET");
549: Reply reply = manager.runRequest(request);
550: // Display some infos:
551: System.out.println("last-modified: "+reply.getLastModified());
552: System.out.println("length : "+reply.getContentLength());
553: // Display the returned body:
554: InputStream in = reply.getInputStream();
555: byte buf[] = new byte[4096];
556: int cnt = 0;
557: while ((cnt = in.read(buf)) > 0)
558: System.out.print(new String(buf, 0, 0, cnt));
559: System.out.println("-");
560: in.close();
561: } catch (Exception ex) {
562: ex.printStackTrace();
563: }
564: System.exit(1);
565: }
566: }
Webmaster