KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > HTTPClient > HTTPConnection


1 /*
2  * @(#)HTTPConnection.java 0.3-2 18/06/1999
3  *
4  * This file is part of the HTTPClient package
5  * Copyright (C) 1996-1999 Ronald Tschalär
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free
19  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  * MA 02111-1307, USA
21  *
22  * For questions, suggestions, bug-reports, enhancement-requests etc.
23  * I may be contacted at:
24  *
25  * ronald@innovation.ch
26  *
27  */

28
29 package HTTPClient;
30
31
32 import java.io.OutputStream JavaDoc;
33 import java.io.DataOutputStream JavaDoc;
34 import java.io.FilterOutputStream JavaDoc;
35 import java.io.ByteArrayOutputStream JavaDoc;
36 import java.io.IOException JavaDoc;
37 import java.io.InterruptedIOException JavaDoc;
38 import java.net.URL JavaDoc;
39 import java.net.Socket JavaDoc;
40 import java.net.InetAddress JavaDoc;
41 import java.net.SocketException JavaDoc;
42 import java.net.UnknownHostException JavaDoc;
43 import java.util.Vector JavaDoc;
44 import java.util.Hashtable JavaDoc;
45 import java.applet.Applet JavaDoc;
46
47
48 /**
49  * This class implements http protocol requests; it contains most of HTTP/1.1
50  * and ought to be unconditionally compliant.
51  * Redirections are automatically handled, and authorizations requests are
52  * recognized and dealt with via an authorization handler.
53  * Only full HTTP/1.0 and HTTP/1.1 requests are generated. HTTP/1.1, HTTP/1.0
54  * and HTTP/0.9 responses are recognized.
55  *
56  * <P>Using the HTTPClient should be quite simple. First add the import
57  * statement '<code>import HTTPClient.*;</code>' to your file(s). Request
58  * can then be sent using one of the methods <var>Head()</var>,
59  * <var>Get()</var>, <var>Post()</var>, etc in <var>HTTPConnection</var>.
60  * These methods all return an instance of <var>HTTPResponse</var> which
61  * has methods for accessing the response headers (<var>getHeader()</var>,
62  * <var>getHeaderAsInt()</var>, etc), various response info
63  * (<var>getStatusCode()</var>, <var>getReasonLine()</var>, etc) and the
64  * reponse data (<var>getData()</var> and <var>getInputStream()</var>).
65  * Following are some examples.
66  *
67  * <P>If this is in an applet you can retrieve files from your server
68  * as follows:
69  *
70  * <PRE>
71  * try
72  * {
73  * HTTPConnection con = new HTTPConnection(this);
74  * HTTPResponse rsp = con.Get("/my_file");
75  * if (rsp.getStatusCode() >= 300)
76  * {
77  * System.err.println("Received Error: "+rsp.getReasonLine());
78  * System.err.println(new String(rsp.getData()));
79  * }
80  * else
81  * data = rsp.getData();
82  *
83  * rsp = con.Get("/another_file");
84  * if (rsp.getStatusCode() >= 300)
85  * {
86  * System.err.println("Received Error: "+rsp.getReasonLine());
87  * System.err.println(new String(rsp.getData()));
88  * }
89  * else
90  * other_data = rsp.getData();
91  * }
92  * catch (IOException ioe)
93  * {
94  * System.err.println(ioe.toString());
95  * }
96  * catch (ModuleException me)
97  * {
98  * System.err.println("Error handling request: " + me.getMessage());
99  * }
100  * </PRE>
101  *
102  * This will get the files "/my_file" and "/another_file" and put their
103  * contents into byte[]s accessible via <code>getData()</code>. Note that
104  * you need to only create a new <var>HTTPConnection</var> when sending a
105  * request to a new server (different host or port); although you may create
106  * a new <var>HTTPConnection</var> for every request to the same server this
107  * <strong>not</strong> recommended, as various information about the server
108  * is cached after the first request (to optimize subsequent requests) and
109  * persistent connections are used whenever possible.
110  *
111  * <P>To POST form data you would use something like this (assuming you
112  * have two fields called <var>name</var> and <var>e-mail</var>, whose
113  * contents are stored in the variables <var>name</var> and <var>email</var>):
114  *
115  * <PRE>
116  * try
117  * {
118  * NVPair form_data[] = new NVPair[2];
119  * form_data[0] = new NVPair("name", name);
120  * form_data[1] = new NVPair("e-mail", email);
121  *
122  * HTTPConnection con = new HTTPConnection(this);
123  * HTTPResponse rsp = con.Post("/cgi-bin/my_script", form_data);
124  * if (rsp.getStatusCode() >= 300)
125  * {
126  * System.err.println("Received Error: "+rsp.getReasonLine());
127  * System.err.println(new String(rsp.getData()));
128  * }
129  * else
130  * stream = rsp.getInputStream();
131  * }
132  * catch (IOException ioe)
133  * {
134  * System.err.println(ioe.toString());
135  * }
136  * catch (ModuleException me)
137  * {
138  * System.err.println("Error handling request: " + me.getMessage());
139  * }
140  * </PRE>
141  *
142  * Here the response data is read at leasure via an <var>InputStream</var>
143  * instead of all at once into a <var>byte[]</var>.
144  *
145  * <P>As another example, if you have a URL you're trying to send a request
146  * to you would do something like the following:
147  *
148  * <PRE>
149  * try
150  * {
151  * URL url = new URL("http://www.mydomain.us/test/my_file");
152  * HTTPConnection con = new HTTPConnection(url);
153  * HTTPResponse rsp = con.Put(url.getFile(), "Hello World");
154  * if (rsp.getStatusCode() >= 300)
155  * {
156  * System.err.println("Received Error: "+rsp.getReasonLine());
157  * System.err.println(new String(rsp.getData()));
158  * }
159  * else
160  * data = rsp.getData();
161  * }
162  * catch (IOException ioe)
163  * {
164  * System.err.println(ioe.toString());
165  * }
166  * catch (ModuleException me)
167  * {
168  * System.err.println("Error handling request: " + me.getMessage());
169  * }
170  * </PRE>
171  *
172  * <P>There are a whole number of methods for each request type; however the
173  * general forms are ([...] means that the enclosed is optional):
174  * <ul>
175  * <li> Head ( file [, form-data [, headers ] ] )
176  * <li> Head ( file [, query [, headers ] ] )
177  * <li> Get ( file [, form-data [, headers ] ] )
178  * <li> Get ( file [, query [, headers ] ] )
179  * <li> Post ( file [, form-data [, headers ] ] )
180  * <li> Post ( file [, data [, headers ] ] )
181  * <li> Post ( file [, stream [, headers ] ] )
182  * <li> Put ( file , data [, headers ] )
183  * <li> Put ( file , stream [, headers ] )
184  * <li> Delete ( file [, headers ] )
185  * <li> Options ( file [, headers [, data] ] )
186  * <li> Options ( file [, headers [, stream] ] )
187  * <li> Trace ( file [, headers ] )
188  * </ul>
189  *
190  * @version 0.3-2 18/06/1999
191  * @author Ronald Tschalär
192  */

193
194 public class HTTPConnection
195     implements GlobalConstants, HTTPClientModuleConstants
196 {
197     /** The current version of this package. */
198     public final static String JavaDoc version = "RPT-HTTPClient/0.3-2";
199
200     /** The default context */
201     private final static Object JavaDoc dflt_context = new Object JavaDoc();
202
203     /** The current context */
204     private Object JavaDoc Context = null;
205
206     /** The protocol used on this connection */
207     private int Protocol;
208
209     /** The server's protocol version; M.m stored as (M<<16 | m) */
210             int ServerProtocolVersion;
211
212     /** Have we gotten the server's protocol version yet? */
213             boolean ServProtVersKnown;
214
215     /** The protocol version we send in a request; this is always HTTP/1.1
216     unless we're talking to a broken server in which case it's HTTP/1.0 */

217     private String JavaDoc RequestProtocolVersion;
218
219     /** hack to force buffering of data instead of using chunked T-E */
220     private static boolean no_chunked = false;
221
222     /** hack to force HTTP/1.0 requests */
223     private static boolean force_1_0 = false;
224
225     /** The remote host this connection is associated with */
226     private String JavaDoc Host;
227
228     /** The remote port this connection is attached to */
229     private int Port;
230
231     /** The current proxy host to use (if any) */
232     private String JavaDoc Proxy_Host = null;
233
234     /** The current proxy port */
235     private int Proxy_Port;
236
237     /** The default proxy host to use (if any) */
238     private static String JavaDoc Default_Proxy_Host = null;
239
240     /** The default proxy port */
241     private static int Default_Proxy_Port;
242
243     /** The list of hosts for which no proxy is to be used */
244     private static CIHashtable non_proxy_host_list = new CIHashtable();
245     private static Vector JavaDoc non_proxy_dom_list = new Vector JavaDoc();
246     private static Vector JavaDoc non_proxy_addr_list = new Vector JavaDoc();
247     private static Vector JavaDoc non_proxy_mask_list = new Vector JavaDoc();
248
249     /** The socks server to use */
250     private SocksClient Socks_client = null;
251
252     /** The default socks server to use */
253     private static SocksClient Default_Socks_client = null;
254
255     /** the current stream demultiplexor */
256     private StreamDemultiplexor input_demux = null;
257
258     /** a list of active stream demultiplexors */
259         LinkedList DemuxList = new LinkedList();
260
261     /** a list of active requests */
262     private LinkedList RequestList = new LinkedList();
263
264     /** does the server support keep-alive's? */
265     private boolean DoesKeepAlive = false;
266
267     /** have we been able to determine the above yet? */
268     private boolean KeepAliveUnknown = true;
269
270     /** the maximum number of requests over a HTTP/1.0 keep-alive connection */
271     private int KeepAliveReqMax = -1;
272
273     /** the number of requests over a HTTP/1.0 keep-alive connection left */
274     private int KeepAliveReqLeft;
275
276     /** hack to be able to disable pipelining */
277     private static boolean NeverPipeline = false;
278
279     /** hack to be able to disable keep-alives */
280     private static boolean NoKeepAlives = false;
281
282     /** hack to work around M$ bug */
283     private static boolean haveMSLargeWritesBug = false;
284
285     /** the default timeout to use for new connections */
286     private static int DefaultTimeout = 0;
287
288     /** the timeout to use for reading responses */
289     private int Timeout;
290
291     /** The list of default http headers */
292     private NVPair[] DefaultHeaders = new NVPair[0];
293
294     /** The default list of modules (as a Vector of Class objects) */
295     private static Vector JavaDoc DefaultModuleList;
296
297     /** The list of modules (as a Vector of Class objects) */
298     private Vector JavaDoc ModuleList;
299
300     /** controls whether modules are allowed to interact with user */
301     private static boolean DefaultAllowUI = true;
302
303     /** controls whether modules are allowed to interact with user */
304     private boolean AllowUI;
305
306
307     static
308     {
309     /*
310      * Let's try and see if we can figure out whether any proxies are
311      * being used.
312      */

313
314     try // JDK 1.1 naming
315
{
316         String JavaDoc host = System.getProperty("http.proxyHost");
317         if (host == null)
318         throw new Exception JavaDoc(); // try JDK 1.0.x naming
319
int port = Integer.getInteger("http.proxyPort", -1).intValue();
320
321         if (DebugConn)
322         System.err.println("Conn: using proxy " + host + ":" + port);
323         setProxyServer(host, port);
324     }
325     catch (Exception JavaDoc e)
326     {
327         try // JDK 1.0.x naming
328
{
329         if (Boolean.getBoolean("proxySet"))
330         {
331             String JavaDoc host = System.getProperty("proxyHost");
332             int port = Integer.getInteger("proxyPort", -1).intValue();
333             if (DebugConn)
334             System.err.println("Conn: using proxy " + host + ":" + port);
335             setProxyServer(host, port);
336         }
337         }
338         catch (Exception JavaDoc ee)
339         { Default_Proxy_Host = null; }
340     }
341
342
343     /*
344      * now check for the non-proxy list
345      */

346     //try
347
//{
348
String JavaDoc hosts = System.getProperty("HTTPClient.nonProxyHosts");
349         if (hosts == null)
350         hosts = System.getProperty("http.nonProxyHosts");
351
352         String JavaDoc[] _list = Util.splitProperty(hosts);
353         dontProxyFor(_list);
354             /*
355               }
356               catch (Exception e)
357               { }
358             */

359
360     /*
361      * we can't turn the JDK SOCKS handling off, so we don't use the
362      * properties 'socksProxyHost' and 'socksProxyPort'. Instead we
363      * define 'HTTPClient.socksHost', 'HTTPClient.socksPort' and
364      * 'HTTPClient.socksVersion'.
365      */

366     try
367     {
368         String JavaDoc host = System.getProperty("HTTPClient.socksHost");
369         if (host != null && host.length() > 0)
370         {
371         int port = Integer.getInteger("HTTPClient.socksPort", -1).intValue();
372         int version = Integer.getInteger("HTTPClient.socksVersion", -1).intValue();
373         if (DebugConn)
374             System.err.println("Conn: using SOCKS " + host + ":" + port);
375         if (version == -1)
376             setSocksServer(host, port);
377         else
378             setSocksServer(host, port, version);
379         }
380     }
381     catch (Exception JavaDoc e)
382         { Default_Socks_client = null; }
383
384
385     // Set up module list
386

387     String JavaDoc modules = "HTTPClient.RetryModule|" +
388              "HTTPClient.CookieModule|" +
389              "HTTPClient.RedirectionModule|" +
390              "HTTPClient.AuthorizationModule|" +
391              "HTTPClient.DefaultModule|" +
392              "HTTPClient.TransferEncodingModule|" +
393              "HTTPClient.ContentMD5Module|" +
394              "HTTPClient.ContentEncodingModule";
395
396     boolean in_applet = false;
397     try
398         { modules = System.getProperty("HTTPClient.Modules", modules); }
399     catch (SecurityException JavaDoc se)
400         { in_applet = true; }
401
402     DefaultModuleList = new Vector JavaDoc();
403     String JavaDoc[] list = Util.splitProperty(modules);
404     for (int idx=0; idx<list.length; idx++)
405     {
406         try
407         {
408         DefaultModuleList.addElement(Class.forName(list[idx]));
409         if (DebugConn)
410             System.err.println("Conn: added module " + list[idx]);
411         }
412         catch (ClassNotFoundException JavaDoc cnfe)
413         {
414         if (!in_applet)
415             throw new NoClassDefFoundError JavaDoc(cnfe.getMessage());
416
417         /* Just ignore it. This allows for example applets to just
418          * load the necessary modules - if you don't need a module
419          * then don't provide it, and it won't be added to the
420          * list. The disadvantage is that if you accidently misstype
421          * a module name this will lead to a "silent" error.
422          */

423         }
424     }
425
426
427     /*
428      * Hack: disable pipelining
429      */

430     try
431     {
432         NeverPipeline = Boolean.getBoolean("HTTPClient.disable_pipelining");
433         if (DebugConn)
434         if (NeverPipeline) System.err.println("Conn: disabling pipelining");
435     }
436     catch (Exception JavaDoc e)
437         { }
438
439     /*
440      * Hack: disable keep-alives
441      */

442     try
443     {
444         NoKeepAlives = Boolean.getBoolean("HTTPClient.disableKeepAlives");
445         if (DebugConn)
446         if (NoKeepAlives) System.err.println("Conn: disabling keep-alives");
447     }
448     catch (Exception JavaDoc e)
449         { }
450
451     /*
452      * Hack: force HTTP/1.0 requests
453      */

454     try
455     {
456         force_1_0 = Boolean.getBoolean("HTTPClient.forceHTTP_1.0");
457         if (DebugConn)
458         if (force_1_0) System.err.println("Conn: forcing HTTP/1.0 requests");
459     }
460     catch (Exception JavaDoc e)
461         { }
462
463     /*
464      * Hack: prevent chunking of request data
465      */

466     try
467     {
468         no_chunked = Boolean.getBoolean("HTTPClient.dontChunkRequests");
469         if (DebugConn)
470         if (no_chunked) System.err.println("Conn: never chunking requests");
471     }
472     catch (Exception JavaDoc e)
473         { }
474
475     /*
476      * M$ bug: large writes hang the stuff
477      */

478     try
479     {
480         if (System.getProperty("os.name").indexOf("Windows") >= 0 &&
481         System.getProperty("java.version").startsWith("1.1"))
482             haveMSLargeWritesBug = true;
483         if (DebugConn)
484         if (haveMSLargeWritesBug)
485             System.err.println("Conn: splitting large writes into 20K chunks (M$ bug)");
486     }
487     catch (Exception JavaDoc e)
488         { }
489     }
490
491
492     // Constructors
493

494     /**
495      * Constructs a connection to the host from where the applet was loaded.
496      * Note that current security policies only let applets connect home.
497      *
498      * @param applet the current applet
499      */

500     public HTTPConnection(Applet JavaDoc applet) throws ProtocolNotSuppException
501     {
502     this(applet.getCodeBase().getProtocol(),
503          applet.getCodeBase().getHost(),
504          applet.getCodeBase().getPort());
505     }
506
507     /**
508      * Constructs a connection to the specified host on port 80
509      *
510      * @param host the host
511      */

512     public HTTPConnection(String JavaDoc host)
513     {
514     Setup(HTTP, host, 80);
515     }
516
517     /**
518      * Constructs a connection to the specified host on the specified port
519      *
520      * @param host the host
521      * @param port the port
522      */

523     public HTTPConnection(String JavaDoc host, int port)
524     {
525     Setup(HTTP, host, port);
526     }
527
528     /**
529      * Constructs a connection to the specified host on the specified port,
530      * using the specified protocol (currently only "http" is supported).
531      *
532      * @param prot the protocol
533      * @param host the host
534      * @param port the port, or -1 for the default port
535      * @exception ProtocolNotSuppException if the protocol is not HTTP
536      */

537     public HTTPConnection(String JavaDoc prot, String JavaDoc host, int port) throws
538     ProtocolNotSuppException
539     {
540     prot = prot.trim().toLowerCase();
541
542     //if (!prot.equals("http") && !prot.equals("https"))
543
if (!prot.equals("http"))
544         throw new ProtocolNotSuppException("Unsupported protocol '" + prot + "'");
545
546     if (prot.equals("http"))
547         Setup(HTTP, host, port);
548     else if (prot.equals("https"))
549         Setup(HTTPS, host, port);
550     else if (prot.equals("shttp"))
551         Setup(SHTTP, host, port);
552     else if (prot.equals("http-ng"))
553         Setup(HTTP_NG, host, port);
554     }
555
556     /**
557      * Constructs a connection to the host (port) as given in the url.
558      *
559      * @param url the url
560      * @exception ProtocolNotSuppException if the protocol is not HTTP
561      */

562     public HTTPConnection(URL JavaDoc url) throws ProtocolNotSuppException
563     {
564     this(url.getProtocol(), url.getHost(), url.getPort());
565     }
566
567     /**
568      * Sets the class variables. Must not be public.
569      *
570      * @param prot the protocol
571      * @param host the host
572      * @param port the port
573      */

574     private void Setup(int prot, String JavaDoc host, int port)
575     {
576     Protocol = prot;
577     Host = host.trim().toLowerCase();
578     Port = port;
579
580     if (Port == -1)
581         Port = URI.defaultPort(getProtocol());
582
583     if (Default_Proxy_Host != null && !matchNonProxy(Host))
584         setCurrentProxy(Default_Proxy_Host, Default_Proxy_Port);
585     else
586         setCurrentProxy(null, 0);
587
588     Socks_client = Default_Socks_client;
589     Timeout = DefaultTimeout;
590     ModuleList = (Vector JavaDoc) DefaultModuleList.clone();
591     AllowUI = DefaultAllowUI;
592     if (NoKeepAlives)
593         setDefaultHeaders(new NVPair[] { new NVPair("Connection", "close") });
594     }
595
596
597     /**
598      * Determines if the given host matches any entry in the non-proxy list.
599      *
600      * @param host the host to match - must be trim()'d and lowercase
601      * @return true if a match is found, false otherwise
602      * @see #dontProxyFor(java.lang.String)
603      */

604     private boolean matchNonProxy(String JavaDoc host)
605     {
606     // Check host name list
607

608     if (non_proxy_host_list.get(host) != null)
609         return true;
610
611
612     // Check domain name list
613

614     for (int idx=0; idx<non_proxy_dom_list.size(); idx++)
615         if (host.endsWith((String JavaDoc) non_proxy_dom_list.elementAt(idx)))
616         return true;
617
618
619     // Check IP-address and subnet list
620

621     if (non_proxy_addr_list.size() == 0)
622         return false;
623
624     InetAddress JavaDoc[] host_addr;
625     try
626         { host_addr = InetAddress.getAllByName(host); }
627     catch (UnknownHostException JavaDoc uhe)
628         { return false; } // maybe the proxy has better luck
629

630     for (int idx=0; idx<non_proxy_addr_list.size(); idx++)
631     {
632         byte[] addr = (byte[]) non_proxy_addr_list.elementAt(idx);
633         byte[] mask = (byte[]) non_proxy_mask_list.elementAt(idx);
634
635         ip_loop: for (int idx2=0; idx2<host_addr.length; idx2++)
636         {
637         byte[] raw_addr = host_addr[idx2].getAddress();
638         if (raw_addr.length != addr.length) continue;
639
640         for (int idx3=0; idx3<raw_addr.length; idx3++)
641         {
642             if ((raw_addr[idx3] & mask[idx3]) != (addr[idx3] & mask[idx3]))
643             continue ip_loop;
644         }
645         return true;
646         }
647     }
648
649     return false;
650     }
651
652
653     // Methods
654

655     /**
656      * Sends the HEAD request. This request is just like the corresponding
657      * GET except that it only returns the headers and no data.
658      *
659      * @see #Get(java.lang.String)
660      * @param file the absolute path of the file
661      * @return an HTTPResponse structure containing the response
662      * @exception java.io.IOException when an exception is returned from
663      * the socket.
664      * @exception ModuleException if an exception is encountered in any module.
665      */

666     public HTTPResponse Head(String JavaDoc file) throws IOException JavaDoc, ModuleException
667     {
668     return Head(file, (String JavaDoc) null, null);
669     }
670
671     /**
672      * Sends the HEAD request. This request is just like the corresponding
673      * GET except that it only returns the headers and no data.
674      *
675      * @see #Get(java.lang.String, HTTPClient.NVPair[])
676      * @param file the absolute path of the file
677      * @param form_data an array of Name/Value pairs
678      * @return an HTTPResponse structure containing the response
679      * @exception java.io.IOException when an exception is returned from
680      * the socket.
681      * @exception ModuleException if an exception is encountered in any module.
682      */

683     public HTTPResponse Head(String JavaDoc file, NVPair form_data[])
684         throws IOException JavaDoc, ModuleException
685     {
686     return Head(file, form_data, null);
687     }
688
689     /**
690      * Sends the HEAD request. This request is just like the corresponding
691      * GET except that it only returns the headers and no data.
692      *
693      * @see #Get(java.lang.String, HTTPClient.NVPair[], HTTPClient.NVPair[])
694      * @param file the absolute path of the file
695      * @param form_data an array of Name/Value pairs
696      * @param headers additional headers
697      * @return an HTTPResponse structure containing the response
698      * @exception java.io.IOException when an exception is returned from
699      * the socket.
700      * @exception ModuleException if an exception is encountered in any module.
701      */

702     public HTTPResponse Head(String JavaDoc file, NVPair[] form_data, NVPair[] headers)
703         throws IOException JavaDoc, ModuleException
704     {
705     String JavaDoc File = stripRef(file),
706            query = Codecs.nv2query(form_data);
707     if (query != null && query.length() > 0)
708         File += "?" + query;
709
710     return setupRequest("HEAD", File, headers, null, null);
711     }
712
713     /**
714      * Sends the HEAD request. This request is just like the corresponding
715      * GET except that it only returns the headers and no data.
716      *
717      * @see #Get(java.lang.String, java.lang.String)
718      * @param file the absolute path of the file
719      * @param query the query string; it will be urlencoded
720      * @return an HTTPResponse structure containing the response
721      * @exception java.io.IOException when an exception is returned from
722      * the socket.
723      * @exception ModuleException if an exception is encountered in any module.
724      */

725     public HTTPResponse Head(String JavaDoc file, String JavaDoc query)
726         throws IOException JavaDoc, ModuleException
727     {
728     return Head(file, query, null);
729     }
730
731
732     /**
733      * Sends the HEAD request. This request is just like the corresponding
734      * GET except that it only returns the headers and no data.
735      *
736      * @see #Get(java.lang.String, java.lang.String, HTTPClient.NVPair[])
737      * @param file the absolute path of the file
738      * @param query the query string; it will be urlencoded
739      * @param headers additional headers
740      * @return an HTTPResponse structure containing the response
741      * @exception java.io.IOException when an exception is returned from
742      * the socket.
743      * @exception ModuleException if an exception is encountered in any module.
744      */

745     public HTTPResponse Head(String JavaDoc file, String JavaDoc query, NVPair[] headers)
746         throws IOException JavaDoc, ModuleException
747     {
748     String JavaDoc File = stripRef(file);
749     if (query != null && query.length() > 0)
750         File += "?" + Codecs.URLEncode(query);
751
752     return setupRequest("HEAD", File, headers, null, null);
753     }
754
755
756     /**
757      * GETs the file.
758      *
759      * @param file the absolute path of the file
760      * @return an HTTPResponse structure containing the response
761      * @exception java.io.IOException when an exception is returned from
762      * the socket.
763      * @exception ModuleException if an exception is encountered in any module.
764      */

765     public HTTPResponse Get(String JavaDoc file) throws IOException JavaDoc, ModuleException
766     {
767     return Get(file, (String JavaDoc) null, null);
768     }
769
770     /**
771      * GETs the file with a query consisting of the specified form-data.
772      * The data is urlencoded, turned into a string of the form
773      * "name1=value1&name2=value2" and then sent as a query string.
774      *
775      * @param file the absolute path of the file
776      * @param form_data an array of Name/Value pairs
777      * @return an HTTPResponse structure containing the response
778      * @exception java.io.IOException when an exception is returned from
779      * the socket.
780      * @exception ModuleException if an exception is encountered in any module.
781      */

782     public HTTPResponse Get(String JavaDoc file, NVPair form_data[])
783         throws IOException JavaDoc, ModuleException
784     {
785     return Get(file, form_data, null);
786     }
787
788     /**
789      * GETs the file with a query consisting of the specified form-data.
790      * The data is urlencoded, turned into a string of the form
791      * "name1=value1&name2=value2" and then sent as a query string.
792      *
793      * @param file the absolute path of the file
794      * @param form_data an array of Name/Value pairs
795      * @param headers additional headers
796      * @return an HTTPResponse structure containing the response
797      * @exception java.io.IOException when an exception is returned from
798      * the socket.
799      * @exception ModuleException if an exception is encountered in any module.
800      */

801     public HTTPResponse Get(String JavaDoc file, NVPair[] form_data, NVPair[] headers)
802         throws IOException JavaDoc, ModuleException
803     {
804     String JavaDoc File = stripRef(file),
805            query = Codecs.nv2query(form_data);
806     if (query != null && query.length() > 0)
807         File += "?" + query;
808
809     return setupRequest("GET", File, headers, null, null);
810     }
811
812     /**
813      * GETs the file using the specified query string. The query string
814      * is first urlencoded.
815      *
816      * @param file the absolute path of the file
817      * @param query the query
818      * @return an HTTPResponse structure containing the response
819      * @exception java.io.IOException when an exception is returned from
820      * the socket.
821      * @exception ModuleException if an exception is encountered in any module.
822      */

823     public HTTPResponse Get(String JavaDoc file, String JavaDoc query)
824         throws IOException JavaDoc, ModuleException
825     {
826     return Get(file, query, null);
827     }
828
829     /**
830      * GETs the file using the specified query string. The query string
831      * is first urlencoded.
832      *
833      * @param file the absolute path of the file
834      * @param query the query string
835      * @param headers additional headers
836      * @return an HTTPResponse structure containing the response
837      * @exception java.io.IOException when an exception is returned from
838      * the socket.
839      * @exception ModuleException if an exception is encountered in any module.
840      */

841     public HTTPResponse Get(String JavaDoc file, String JavaDoc query, NVPair[] headers)
842         throws IOException JavaDoc, ModuleException
843     {
844     String JavaDoc File = stripRef(file);
845     if (query != null && query.length() > 0)
846         File += "?" + Codecs.URLEncode(query);
847
848     return setupRequest("GET", File, headers, null, null);
849     }
850
851
852     /**
853      * POSTs to the specified file. No data is sent.
854      *
855      * @param file the absolute path of the file
856      * @return an HTTPResponse structure containing the response
857      * @exception java.io.IOException when an exception is returned from
858      * the socket.
859      * @exception ModuleException if an exception is encountered in any module.
860      */

861     public HTTPResponse Post(String JavaDoc file) throws IOException JavaDoc, ModuleException
862     {
863     return Post(file, (byte []) null, null);
864     }
865
866     /**
867      * POSTs form-data to the specified file. The data is first urlencoded
868      * and then turned into a string of the form "name1=value1&name2=value2".
869      * A <var>Content-type</var> header with the value
870      * <var>application/x-www-form-urlencoded</var> is added.
871      *
872      * @param file the absolute path of the file
873      * @param form_data an array of Name/Value pairs
874      * @return an HTTPResponse structure containing the response
875      * @exception java.io.IOException when an exception is returned from
876      * the socket.
877      * @exception ModuleException if an exception is encountered in any module.
878      */

879     public HTTPResponse Post(String JavaDoc file, NVPair form_data[])
880         throws IOException JavaDoc, ModuleException
881     {
882     NVPair[] headers =
883         { new NVPair("Content-type", "application/x-www-form-urlencoded") };
884
885     return Post(file, Codecs.nv2query(form_data), headers);
886     }
887
888     /**
889      * POST's form-data to the specified file using the specified headers.
890      * The data is first urlencoded and then turned into a string of the
891      * form "name1=value1&name2=value2". If no <var>Content-type</var> header
892      * is given then one is added with a value of
893      * <var>application/x-www-form-urlencoded</var>.
894      *
895      * @param file the absolute path of the file
896      * @param form_data an array of Name/Value pairs
897      * @param headers additional headers
898      * @return a HTTPResponse structure containing the response
899      * @exception java.io.IOException when an exception is returned from
900      * the socket.
901      * @exception ModuleException if an exception is encountered in any module.
902      */

903     public HTTPResponse Post(String JavaDoc file, NVPair form_data[], NVPair headers[])
904                 throws IOException JavaDoc, ModuleException
905     {
906     int idx;
907     for (idx=0; idx<headers.length; idx++)
908         if (headers[idx].getName().equalsIgnoreCase("Content-type")) break;
909     if (idx == headers.length)
910     {
911         headers = Util.resizeArray(headers, idx+1);
912         headers[idx] =
913         new NVPair("Content-type", "application/x-www-form-urlencoded");
914     }
915
916     return Post(file, Codecs.nv2query(form_data), headers);
917     }
918
919     /**
920      * POSTs the data to the specified file. The data is converted to an
921      * array of bytes using the lower byte of each character.
922      * The request is sent using the content-type "application/octet-stream".
923      *
924      * @see java.lang.String#getBytes(int, int, byte[], int)
925      * @param file the absolute path of the file
926      * @param data the data
927      * @return an HTTPResponse structure containing the response
928      * @exception java.io.IOException when an exception is returned from
929      * the socket.
930      * @exception ModuleException if an exception is encountered in any module.
931      */

932     public HTTPResponse Post(String JavaDoc file, String JavaDoc data)
933         throws IOException JavaDoc, ModuleException
934     {
935     return Post(file, data, null);
936     }
937
938     /**
939      * POSTs the data to the specified file using the specified headers.
940      *
941      * @param file the absolute path of the file
942      * @param data the data
943      * @param headers additional headers
944      * @return an HTTPResponse structure containing the response
945      * @exception java.io.IOException when an exception is returned from
946      * the socket.
947      * @exception ModuleException if an exception is encountered in any module.
948      */

949     public HTTPResponse Post(String JavaDoc file, String JavaDoc data, NVPair[] headers)
950         throws IOException JavaDoc, ModuleException
951     {
952     byte tmp[] = null;
953
954     if (data != null && data.length() > 0)
955     {
956         tmp = new byte[data.length()];
957         data.getBytes(0, data.length(), tmp, 0);
958     }
959
960     return Post(file, tmp, headers);
961     }
962
963     /**
964      * POSTs the raw data to the specified file.
965      * The request is sent using the content-type "application/octet-stream"
966      *
967      * @param file the absolute path of the file
968      * @param data the data
969      * @return an HTTPResponse structure containing the response
970      * @exception java.io.IOException when an exception is returned from
971      * the socket.
972      * @exception ModuleException if an exception is encountered in any module.
973      */

974     public HTTPResponse Post(String JavaDoc file, byte data[])
975         throws IOException JavaDoc, ModuleException
976     {
977     return Post(file, data, null);
978     }
979
980     /**
981      * POSTs the raw data to the specified file using the specified headers.
982      *
983      * @param file the absolute path of the file
984      * @param data the data
985      * @param headers additional headers
986      * @return an HTTPResponse structure containing the response
987      * @exception java.io.IOException when an exception is returned from
988      * the socket.
989      * @exception ModuleException if an exception is encountered in any module.
990      */

991     public HTTPResponse Post(String JavaDoc file, byte data[], NVPair[] headers)
992         throws IOException JavaDoc, ModuleException
993     {
994     if (data == null) data = new byte[0]; // POST must always have a CL
995
return setupRequest("POST", stripRef(file), headers, data, null);
996     }
997
998
999     /**
1000     * POSTs the data written to the output stream to the specified file.
1001     * The request is sent using the content-type "application/octet-stream"
1002     *
1003     * @param file the absolute path of the file
1004     * @param stream the output stream on which the data is written
1005     * @return an HTTPResponse structure containing the response
1006     * @exception java.io.IOException when an exception is returned from
1007     * the socket.
1008     * @exception ModuleException if an exception is encountered in any module.
1009     */

1010    public HTTPResponse Post(String JavaDoc file, HttpOutputStream stream)
1011        throws IOException JavaDoc, ModuleException
1012    {
1013    return Post(file, stream, null);
1014    }
1015
1016    /**
1017     * POSTs the data written to the output stream to the specified file
1018     * using the specified headers.
1019     *
1020     * @param file the absolute path of the file
1021     * @param stream the output stream on which the data is written
1022     * @param headers additional headers
1023     * @return an HTTPResponse structure containing the response
1024     * @exception java.io.IOException when an exception is returned from
1025     * the socket.
1026     * @exception ModuleException if an exception is encountered in any module.
1027     */

1028    public HTTPResponse Post(String JavaDoc file, HttpOutputStream stream,
1029                 NVPair[] headers)
1030        throws IOException JavaDoc, ModuleException
1031    {
1032    return setupRequest("POST", stripRef(file), headers, null, stream);
1033    }
1034
1035
1036    /**
1037     * PUTs the data into the specified file. The data is converted to an
1038     * array of bytes using the lower byte of each character.
1039     * The request ist sent using the content-type "application/octet-stream".
1040     *
1041     * @see java.lang.String#getBytes(int, int, byte[], int)
1042     * @param file the absolute path of the file
1043     * @param data the data
1044     * @return an HTTPResponse structure containing the response
1045     * @exception java.io.IOException when an exception is returned from
1046     * the socket.
1047     * @exception ModuleException if an exception is encountered in any module.
1048     */

1049    public HTTPResponse Put(String JavaDoc file, String JavaDoc data)
1050        throws IOException JavaDoc, ModuleException
1051    {
1052    return Put(file, data, null);
1053    }
1054
1055    /**
1056     * PUTs the data into the specified file using the additional headers
1057     * for the request.
1058     *
1059     * @param file the absolute path of the file
1060     * @param data the data
1061     * @param headers additional headers
1062     * @return an HTTPResponse structure containing the response
1063     * @exception java.io.IOException when an exception is returned from
1064     * the socket.
1065     * @exception ModuleException if an exception is encountered in any module.
1066     */

1067    public HTTPResponse Put(String JavaDoc file, String JavaDoc data, NVPair[] headers)
1068        throws IOException JavaDoc, ModuleException
1069    {
1070    byte tmp[] = null;
1071
1072    if (data != null)
1073    {
1074        tmp = new byte[data.length()];
1075        data.getBytes(0, data.length(), tmp, 0);
1076    }
1077
1078    return Put(file, tmp, headers);
1079    }
1080
1081    /**
1082     * PUTs the raw data into the specified file.
1083     * The request is sent using the content-type "application/octet-stream".
1084     *
1085     * @param file the absolute path of the file
1086     * @param data the data
1087     * @return an HTTPResponse structure containing the response
1088     * @exception java.io.IOException when an exception is returned from
1089     * the socket.
1090     * @exception ModuleException if an exception is encountered in any module.
1091     */

1092    public HTTPResponse Put(String JavaDoc file, byte data[])
1093        throws IOException JavaDoc, ModuleException
1094    {
1095    return Put(file, data, null);
1096    }
1097
1098    /**
1099     * PUTs the raw data into the specified file using the additional
1100     * headers.
1101     *
1102     * @param file the absolute path of the file
1103     * @param data the data
1104     * @param headers any additional headers
1105     * @return an HTTPResponse structure containing the response
1106     * @exception java.io.IOException when an exception is returned from
1107     * the socket.
1108     * @exception ModuleException if an exception is encountered in any module.
1109     */

1110    public HTTPResponse Put(String JavaDoc file, byte data[], NVPair[] headers)
1111        throws IOException JavaDoc, ModuleException
1112    {
1113    if (data == null) data = new byte[0]; // PUT must always have a CL
1114
return setupRequest("PUT", stripRef(file), headers, data, null);
1115    }
1116
1117    /**
1118     * PUTs the data written to the output stream into the specified file.
1119     * The request is sent using the content-type "application/octet-stream".
1120     *
1121     * @param file the absolute path of the file
1122     * @param stream the output stream on which the data is written
1123     * @return an HTTPResponse structure containing the response
1124     * @exception java.io.IOException when an exception is returned from
1125     * the socket.
1126     * @exception ModuleException if an exception is encountered in any module.
1127     */

1128    public HTTPResponse Put(String JavaDoc file, HttpOutputStream stream)
1129        throws IOException JavaDoc, ModuleException
1130    {
1131    return Put(file, stream, null);
1132    }
1133
1134    /**
1135     * PUTs the data written to the output stream into the specified file
1136     * using the additional headers.
1137     *
1138     * @param file the absolute path of the file
1139     * @param stream the output stream on which the data is written
1140     * @param headers any additional headers
1141     * @return an HTTPResponse structure containing the response
1142     * @exception java.io.IOException when an exception is returned from
1143     * the socket.
1144     * @exception ModuleException if an exception is encountered in any module.
1145     */

1146    public HTTPResponse Put(String JavaDoc file, HttpOutputStream stream,
1147                NVPair[] headers)
1148        throws IOException JavaDoc, ModuleException
1149    {
1150    return setupRequest("PUT", stripRef(file), headers, null, stream);
1151    }
1152
1153
1154    /**
1155     * Request OPTIONS from the server. If <var>file</var> is "*" then
1156     * the request applies to the server as a whole; otherwise it applies
1157     * only to that resource.
1158     *
1159     * @param file the absolute path of the resource, or "*"
1160     * @return an HTTPResponse structure containing the response
1161     * @exception java.io.IOException when an exception is returned from
1162     * the socket.
1163     * @exception ModuleException if an exception is encountered in any module.
1164     */

1165    public HTTPResponse Options(String JavaDoc file)
1166        throws IOException JavaDoc, ModuleException
1167    {
1168    return Options(file, null, (byte[]) null);
1169    }
1170
1171
1172    /**
1173     * Request OPTIONS from the server. If <var>file</var> is "*" then
1174     * the request applies to the server as a whole; otherwise it applies
1175     * only to that resource.
1176     *
1177     * @param file the absolute path of the resource, or "*"
1178     * @param headers the headers containing optional info.
1179     * @return an HTTPResponse structure containing the response
1180     * @exception java.io.IOException when an exception is returned from
1181     * the socket.
1182     * @exception ModuleException if an exception is encountered in any module.
1183     */

1184    public HTTPResponse Options(String JavaDoc file, NVPair[] headers)
1185        throws IOException JavaDoc, ModuleException
1186    {
1187    return Options(file, headers, (byte[]) null);
1188    }
1189
1190
1191    /**
1192     * Request OPTIONS from the server. If <var>file</var> is "*" then
1193     * the request applies to the server as a whole; otherwise it applies
1194     * only to that resource.
1195     *
1196     * @param file the absolute path of the resource, or "*"
1197     * @param headers the headers containing optional info.
1198     * @param data any data to be sent in the optional body
1199     * @return an HTTPResponse structure containing the response
1200     * @exception java.io.IOException when an exception is returned from
1201     * the socket.
1202     * @exception ModuleException if an exception is encountered in any module.
1203     */

1204    public HTTPResponse Options(String JavaDoc file, NVPair[] headers, byte[] data)
1205        throws IOException JavaDoc, ModuleException
1206    {
1207    return setupRequest("OPTIONS", stripRef(file), headers, data, null);
1208    }
1209
1210
1211    /**
1212     * Request OPTIONS from the server. If <var>file</var> is "*" then
1213     * the request applies to the server as a whole; otherwise it applies
1214     * only to that resource.
1215     *
1216     * @param file the absolute path of the resource, or "*"
1217     * @param headers the headers containing optional info.
1218     * @param stream an output stream for sending the optional body
1219     * @return an HTTPResponse structure containing the response
1220     * @exception java.io.IOException when an exception is returned from
1221     * the socket.
1222     * @exception ModuleException if an exception is encountered in any module.
1223     */

1224    public HTTPResponse Options(String JavaDoc file, NVPair[] headers,
1225                HttpOutputStream stream)
1226        throws IOException JavaDoc, ModuleException
1227    {
1228    return setupRequest("OPTIONS", stripRef(file), headers, null, stream);
1229    }
1230
1231
1232    /**
1233     * Requests that <var>file</var> be DELETEd from the server.
1234     *
1235     * @param file the absolute path of the resource
1236     * @return an HTTPResponse structure containing the response
1237     * @exception java.io.IOException when an exception is returned from
1238     * the socket.
1239     * @exception ModuleException if an exception is encountered in any module.
1240     */

1241    public HTTPResponse Delete(String JavaDoc file)
1242        throws IOException JavaDoc, ModuleException
1243    {
1244    return Delete(file, null);
1245    }
1246
1247
1248    /**
1249     * Requests that <var>file</var> be DELETEd from the server.
1250     *
1251     * @param file the absolute path of the resource
1252     * @param headers additional headers
1253     * @return an HTTPResponse structure containing the response
1254     * @exception java.io.IOException when an exception is returned from
1255     * the socket.
1256     * @exception ModuleException if an exception is encountered in any module.
1257     */

1258    public HTTPResponse Delete(String JavaDoc file, NVPair[] headers)
1259        throws IOException JavaDoc, ModuleException
1260    {
1261    return setupRequest("DELETE", stripRef(file), headers, null, null);
1262    }
1263
1264
1265    /**
1266     * Requests a TRACE. Headers of particular interest here are "Via"
1267     * and "Max-Forwards".
1268     *
1269     * @param file the absolute path of the resource
1270     * @param headers additional headers
1271     * @return an HTTPResponse structure containing the response
1272     * @exception java.io.IOException when an exception is returned from
1273     * the socket.
1274     * @exception ModuleException if an exception is encountered in any module.
1275     */

1276    public HTTPResponse Trace(String JavaDoc file, NVPair[] headers)
1277        throws IOException JavaDoc, ModuleException
1278    {
1279    return setupRequest("TRACE", stripRef(file), headers, null, null);
1280    }
1281
1282
1283    /**
1284     * Requests a TRACE.
1285     *
1286     * @param file the absolute path of the resource
1287     * @return an HTTPResponse structure containing the response
1288     * @exception java.io.IOException when an exception is returned from
1289     * the socket.
1290     * @exception ModuleException if an exception is encountered in any module.
1291     */

1292    public HTTPResponse Trace(String JavaDoc file)
1293        throws IOException JavaDoc, ModuleException
1294    {
1295    return Trace(file, null);
1296    }
1297
1298
1299    /**
1300     * This is here to allow an arbitrary, non-standard request to be sent.
1301     * I'm assuming you know what you are doing...
1302     *
1303     * @param method the extension method
1304     * @param file the absolute path of the resource, or null
1305     * @param data optional data, or null
1306     * @param headers optional headers, or null
1307     * @return an HTTPResponse structure containing the response
1308     * @exception java.io.IOException when an exception is returned from
1309     * the socket.
1310     * @exception ModuleException if an exception is encountered in any module.
1311     */

1312    public HTTPResponse ExtensionMethod(String JavaDoc method, String JavaDoc file,
1313                    byte[] data, NVPair[] headers)
1314        throws IOException JavaDoc, ModuleException
1315    {
1316    return setupRequest(method.trim(), stripRef(file), headers, data, null);
1317    }
1318
1319
1320    /**
1321     * This is here to allow an arbitrary, non-standard request to be sent.
1322     * I'm assuming you know what you are doing...
1323     *
1324     * @param method the extension method
1325     * @param file the absolute path of the resource, or null
1326     * @param stream optional output stream, or null
1327     * @param headers optional headers, or null
1328     * @return an HTTPResponse structure containing the response
1329     * @exception java.io.IOException when an exception is returned from
1330     * the socket.
1331     * @exception ModuleException if an exception is encountered in any module.
1332     */

1333    public HTTPResponse ExtensionMethod(String JavaDoc method, String JavaDoc file,
1334                    HttpOutputStream os, NVPair[] headers)
1335        throws IOException JavaDoc, ModuleException
1336    {
1337    return setupRequest(method.trim(), stripRef(file), headers, null, os);
1338    }
1339
1340
1341    /**
1342     * Aborts all the requests currently in progress on this connection and
1343     * closes all associated sockets.
1344     *
1345     * <P>Note: there is a small window where a request method such as
1346     * <code>Get()</code> may have been invoked but the request has not
1347     * been built and added to the list. Any request in this window will
1348     * not be aborted.
1349     *
1350     * @since V0.2-3
1351     */

1352    public void stop()
1353    {
1354    for (Request req = (Request) RequestList.enumerate(); req != null;
1355         req = (Request) RequestList.next())
1356        req.aborted = true;
1357
1358    for (StreamDemultiplexor demux =
1359                (StreamDemultiplexor) DemuxList.enumerate();
1360         demux != null; demux = (StreamDemultiplexor) DemuxList.next())
1361        demux.abort();
1362    }
1363
1364
1365    /**
1366     * Sets the default http headers to be sent with each request. The
1367     * actual headers sent are determined as follows: for each header
1368     * specified in multiple places a value given as part of the request
1369     * takes priority over any default values set by this method, which
1370     * in turn takes priority over any built-in default values. A different
1371     * way of looking at it is that we start off with a list of all headers
1372     * specified with the request, then add any default headers set by this
1373     * method which aren't already in our list, and finally add any built-in
1374     * headers which aren't yet in the list. There are two exceptions to this
1375     * rule: "Content-length" and "Host" headers are always ignored; and when
1376     * posting form-data any default "Content-type" is ignored in favor of
1377     * the built-in "application/x-www-form-urlencoded" (however it will be
1378     * overriden by any content-type header specified as part of the request).
1379     *
1380     * <P>Typical headers you might want to set here are "Accept" and its
1381     * "Accept-*" relatives, "Connection", "From", "User-Agent", etc.
1382     *
1383     * @param headers an array of header-name/value pairs (do not give the
1384     * separating ':').
1385     */

1386    public void setDefaultHeaders(NVPair[] headers)
1387    {
1388    int length = (headers == null ? 0 : headers.length);
1389    NVPair[] def_hdrs = new NVPair[length];
1390
1391    // weed out undesired headers
1392
int sidx, didx;
1393    for (sidx=0, didx=0; sidx<length; sidx++)
1394    {
1395        String JavaDoc name = headers[sidx].getName().trim();
1396        if (name.equalsIgnoreCase("Content-length") ||
1397        name.equalsIgnoreCase("Host"))
1398        continue;
1399
1400        def_hdrs[didx++] = headers[sidx];
1401    }
1402
1403    if (didx < length)
1404        def_hdrs = Util.resizeArray(DefaultHeaders, didx);
1405
1406    synchronized (DefaultHeaders)
1407        { DefaultHeaders = def_hdrs; }
1408    }
1409
1410
1411    /**
1412     * Gets the current list of default http headers.
1413     *
1414     * @return an array of header/value pairs.
1415     */

1416    public NVPair[] getDefaultHeaders()
1417    {
1418    //return (NVPair[]) DefaultHeaders.clone(); JDK 1.1 Only
1419

1420    synchronized (DefaultHeaders)
1421    {
1422        NVPair[] headers = new NVPair[DefaultHeaders.length];
1423        System.arraycopy(DefaultHeaders, 0, headers, 0, headers.length);
1424        return headers;
1425    }
1426    }
1427
1428
1429    /**
1430     * Returns the protocol this connection is talking.
1431     *
1432     * @return a string containing the (lowercased) protocol
1433     */

1434    public String JavaDoc getProtocol()
1435    {
1436    switch (Protocol)
1437    {
1438        case HTTP: return "http";
1439        case HTTPS: return "https";
1440        case SHTTP: return "shttp";
1441        case HTTP_NG: return "http-ng";
1442        default:
1443        throw new Error JavaDoc("HTTPClient Internal Error: invalid protocol " +
1444                Protocol);
1445    }
1446    }
1447
1448
1449    /**
1450     * Returns the host this connection is talking to.
1451     *
1452     * @return a string containing the (lowercased) host name.
1453     */

1454    public String JavaDoc getHost()
1455    {
1456    return Host;
1457    }
1458
1459
1460    /**
1461     * Returns the port this connection connects to. This is always the
1462     * actual port number, never -1.
1463     *
1464     * @return the port number
1465     */

1466    public int getPort()
1467    {
1468    return Port;
1469    }
1470
1471
1472    /**
1473     * Returns the host of the proxy this connection is using.
1474     *
1475     * @return a string containing the (lowercased) host name.
1476     */

1477    public String JavaDoc getProxyHost()
1478    {
1479    return Proxy_Host;
1480    }
1481
1482
1483    /**
1484     * Returns the port of the proxy this connection is using.
1485     *
1486     * @return the port number
1487     */

1488    public int getProxyPort()
1489    {
1490    return Proxy_Port;
1491    }
1492
1493
1494    /**
1495     * See if the given uri is compatible with this connection. Compatible
1496     * means that the given uri can be retrieved using this connection
1497     * object.
1498     *
1499     * @param uri the URI to check
1500     * @return true if they're compatible, false otherwise
1501     * @since V0.3-2
1502     */

1503    public boolean isCompatibleWith(URI uri)
1504    {
1505    if (!uri.getScheme().equals(getProtocol()) ||
1506        !uri.getHost().equalsIgnoreCase(Host))
1507        return false;
1508
1509    int port = uri.getPort();
1510    if (port == -1)
1511        port = URI.defaultPort(uri.getScheme());
1512    return port == Port;
1513    }
1514
1515
1516    /**
1517     * Sets/Resets raw mode. In raw mode all modules are bypassed, meaning
1518     * the automatic handling of authorization requests, redirections,
1519     * cookies, etc. is turned off.
1520     *
1521     * <P>The default is false.
1522     *
1523     * @deprecated This is not really needed anymore; in V0.2 request were
1524     * synchronous and therefore to do pipelining you needed
1525     * to disable the processing of responses.
1526     * @see #removeModule(java.lang.Class)
1527     *
1528     * @param raw if true removes all modules (except for the retry module)
1529     */

1530    public void setRawMode(boolean raw)
1531    {
1532    // Don't remove the retry module
1533
String JavaDoc[] modules = { "HTTPClient.CookieModule",
1534                 "HTTPClient.RedirectionModule",
1535                 "HTTPClient.AuthorizationModule",
1536                 "HTTPClient.DefaultModule",
1537                 "HTTPClient.TransferEncodingModule",
1538                 "HTTPClient.ContentMD5Module",
1539                 "HTTPClient.ContentEncodingModule"};
1540
1541    for (int idx=0; idx<modules.length; idx++)
1542    {
1543        try
1544        {
1545        if (raw)
1546            removeModule(Class.forName(modules[idx]));
1547        else
1548            addModule(Class.forName(modules[idx]), -1);
1549        }
1550        catch (ClassNotFoundException JavaDoc cnfe) { }
1551    }
1552    }
1553
1554
1555    /**
1556     * Sets the default timeout value to be used for each new HTTPConnection.
1557     * The default is 0.
1558     *
1559     * @param time the timeout in milliseconds.
1560     * @see #setTimeout(int)
1561     */

1562    public static void setDefaultTimeout(int time)
1563    {
1564    DefaultTimeout = time;
1565    }
1566
1567
1568    /**
1569     * Gets the default timeout value to be used for each new HTTPConnection.
1570     *
1571     * @return the timeout in milliseconds.
1572     * @see #setTimeout(int)
1573     */

1574    public static int getDefaultTimeout()
1575    {
1576    return DefaultTimeout;
1577    }
1578
1579
1580    /**
1581     * Sets the timeout to be used for creating connections and reading
1582     * responses. When a timeout expires the operation will throw an
1583     * InterruptedIOException. The operation may be restarted again
1584     * afterwards. If the operation is not restarted and it is a read
1585     * operation (i.e HTTPResponse.xxxx()) then <code>stop()</code>
1586     * <strong>should</strong> be invoked.
1587     *
1588     * <P>When creating new sockets the timeout will limit the time spent
1589     * doing the host name translation and establishing the connection with
1590     * the server.
1591     *
1592     * <P>The timeout also influences the reading of the response headers.
1593     * However, it does not specify a how long, for example, getStatusCode()
1594     * may take, as might be assumed. Instead it specifies how long a read
1595     * on the socket may take. If the response dribbles in slowly with
1596     * packets arriving quicker than the timeout then the method will
1597     * complete normally. I.e. the exception is only thrown if nothing
1598     * arrives on the socket for the specified time. Furthermore, the
1599     * timeout only influences the reading of the headers, not the reading
1600     * of the body.
1601     *
1602     * <P>Read Timeouts are associated with responses, so that you may change
1603     * this value before each request and it won't affect the reading of
1604     * responses to previous requests.
1605     *
1606     * <P><em>Note:</em> The read timeout only works with JDK 1.1 or later.
1607     * Using this method with JDK 1.0.2 or earlier will only influence the
1608     * connection creation.
1609     *
1610     * @param time the time in milliseconds. A time of 0 means wait
1611     * indefinitely.
1612     * @see #stop()
1613     */

1614    public void setTimeout(int time)
1615    {
1616    Timeout = time;
1617    }
1618
1619
1620    /**
1621     * Gets the timeout used for reading response data.
1622     *
1623     * @return the current timeout value
1624     * @see #setTimeout(int)
1625     */

1626    public int getTimeout()
1627    {
1628    return Timeout;
1629    }
1630
1631
1632    /**
1633     * Controls whether modules are allowed to prompt the user or pop up
1634     * dialogs if neccessary.
1635     *
1636     * @param allow if true allows modules to interact with user.
1637     */

1638    public void setAllowUserInteraction(boolean allow)
1639    {
1640    AllowUI = allow;
1641    }
1642
1643    /**
1644     * returns whether modules are allowed to prompt or popup dialogs
1645     * if neccessary.
1646     *
1647     * @return true if modules are allowed to interact with user.
1648     */

1649    public boolean getAllowUserInteraction()
1650    {
1651    return AllowUI;
1652    }
1653
1654
1655    /**
1656     * Sets the default allow-user-action.
1657     *
1658     * @param allow if true allows modules to interact with user.
1659     */

1660    public static void setDefaultAllowUserInteraction(boolean allow)
1661    {
1662    DefaultAllowUI = allow;
1663    }
1664
1665    /**
1666     * Gets the default allow-user-action.
1667     *
1668     * @return true if modules are allowed to interact with user.
1669     */

1670    public static boolean getDefaultAllowUserInteraction()
1671    {
1672    return DefaultAllowUI;
1673    }
1674
1675
1676    /**
1677     * Returns the default list of modules.
1678     *
1679     * @return an array of classes
1680     */

1681    public static Class JavaDoc[] getDefaultModules()
1682    {
1683    synchronized(DefaultModuleList)
1684    {
1685        Class JavaDoc[] modules = new Class JavaDoc[DefaultModuleList.size()];
1686        DefaultModuleList.copyInto(modules);
1687        return modules;
1688    }
1689    }
1690
1691    /**
1692     * Adds a module to the default list. It must implement the
1693     * <var>HTTPClientModule</var> interface. If the module is already in
1694     * the list then this method does nothing.
1695     *
1696     * <P>Example:
1697     * <PRE>
1698     * HTTPConnection.addDefaultModule(Class.forName("HTTPClient.CookieModule"), 1);
1699     * </PRE>
1700     * adds the cookie module as the second module in the list.
1701     *
1702     * <P>The default list is created at class initialization time from the
1703     * property <var>HTTPClient.Modules</var>. This must contain a "|"
1704     * separated list of classes in the order they're to be invoked. If this
1705     * property is not set it defaults to:
1706     *
1707     * "HTTPClient.RetryModule | HTTPClient.CookieModule |
1708     * HTTPClient.RedirectionModule | HTTPClient.AuthorizationModule |
1709     * HTTPClient.DefaultModule | HTTPClient.TransferEncodingModule |
1710     * HTTPClient.ContentMD5Module | HTTPClient.ContentEncodingModule"
1711     *
1712     * @see HTTPClientModule
1713     * @param module the module's Class object
1714     * @param pos the position of this module in the list; if <var>pos</var>
1715     * >= 0 then this is the absolute position in the list (0 is
1716     * the first position); if <var>pos</var> < 0 then this is
1717     * the position relative to the end of the list (-1 means
1718     * the last element, -2 the second to last element, etc).
1719     * @return true if module was successfully added; false if the
1720     * module is already in the list.
1721     * @exception ArrayIndexOutOfBoundsException if <var>pos</var> >
1722     * list-size or if <var>pos</var> < -(list-size).
1723     * @exception ClassCastException if <var>module</var> does not
1724     * implement the <var>HTTPClientModule</var> interface.
1725     * @exception RuntimeException if <var>module</var> cannot be
1726     * instantiated.
1727     */

1728    public static boolean addDefaultModule(Class JavaDoc module, int pos)
1729    {
1730    // check if module implements HTTPClientModule
1731
try
1732        { HTTPClientModule tmp = (HTTPClientModule) module.newInstance(); }
1733    catch (RuntimeException JavaDoc re)
1734        { throw re; }
1735    catch (Exception JavaDoc e)
1736        { throw new RuntimeException JavaDoc(e.toString()); }
1737
1738    synchronized(DefaultModuleList)
1739    {
1740        // check if module already in list
1741
if (DefaultModuleList.contains(module))
1742        return false;
1743
1744        // add module to list
1745
if (pos < 0)
1746        DefaultModuleList.insertElementAt(module,
1747                          DefaultModuleList.size()+pos+1);
1748        else
1749        DefaultModuleList.insertElementAt(module, pos);
1750    }
1751
1752    if (DebugConn)
1753        System.err.println("Conn: Added module " + module.getName() +
1754                   " to default list");
1755
1756    return true;
1757    }
1758
1759
1760    /**
1761     * Removes a module from the default list. If the module is not in the
1762     * list it does nothing.
1763     *
1764     * @param module the module's Class object
1765     * @return true if module was successfully removed; false otherwise
1766     */

1767    public static boolean removeDefaultModule(Class JavaDoc module)
1768    {
1769    boolean removed = DefaultModuleList.removeElement(module);
1770
1771    if (DebugConn)
1772        if (removed)
1773        System.err.println("Conn: Removed module " + module.getName() +
1774                   " from default list");
1775
1776    return removed;
1777    }
1778
1779
1780    /**
1781     * Returns the list of modules used currently.
1782     *
1783     * @return an array of classes
1784     */

1785    public Class JavaDoc[] getModules()
1786    {
1787    synchronized(ModuleList)
1788    {
1789        Class JavaDoc[] modules = new Class JavaDoc[ModuleList.size()];
1790        ModuleList.copyInto(modules);
1791        return modules;
1792    }
1793    }
1794
1795
1796    /**
1797     * Adds a module to the current list. It must implement the
1798     * <var>HTTPClientModule</var> interface. If the module is already in
1799     * the list then this method does nothing.
1800     *
1801     * @see HTTPClientModule
1802     * @param module the module's Class object
1803     * @param pos the position of this module in the list; if <var>pos</var>
1804     * >= 0 then this is the absolute position in the list (0 is
1805     * the first position); if <var>pos</var> < 0 then this is
1806     * the position relative to the end of the list (-1 means
1807     * the last element, -2 the second to last element, etc).
1808     * @return true if module was successfully added; false if the
1809     * module is already in the list.
1810     * @exception ArrayIndexOutOfBoundsException if <var>pos</var> >
1811     * list-size or if <var>pos</var> < -(list-size).
1812     * @exception ClassCastException if <var>module</var> does not
1813     * implement the <var>HTTPClientModule</var> interface.
1814     * @exception RuntimeException if <var>module</var> cannot be
1815     * instantiated.
1816     */

1817    public boolean addModule(Class JavaDoc module, int pos)
1818    {
1819    // check if module implements HTTPClientModule
1820
try
1821        { HTTPClientModule tmp = (HTTPClientModule) module.newInstance(); }
1822    catch (RuntimeException JavaDoc re)
1823        { throw re; }
1824    catch (Exception JavaDoc e)
1825        { throw new RuntimeException JavaDoc(e.toString()); }
1826
1827    synchronized(ModuleList)
1828    {
1829        // check if module already in list
1830
if (ModuleList.contains(module))
1831        return false;
1832
1833        // add module to list
1834
if (pos < 0)
1835        ModuleList.insertElementAt(module, ModuleList.size()+pos+1);
1836        else
1837        ModuleList.insertElementAt(module, pos);
1838    }
1839
1840    return true;
1841    }
1842
1843
1844    /**
1845     * Removes a module from the current list. If the module is not in the
1846     * list it does nothing.
1847     *
1848     * @param module the module's Class object
1849     * @return true if module was successfully removed; false otherwise
1850     */

1851    public boolean removeModule(Class JavaDoc module)
1852    {
1853    if (module == null) return false;
1854    return ModuleList.removeElement(module);
1855    }
1856
1857
1858    /**
1859     * Sets the current context. The context is used by modules such as
1860     * the AuthorizationModule and the CookieModule which keep lists of
1861     * info that is normally shared between all instances of HTTPConnection.
1862     * This is usually the desired behaviour. However, in some cases one
1863     * would like to simulate multiple independent clients within the
1864     * same application and hence the sharing of such info should be
1865     * restricted. This is where the context comes in. Modules will only
1866     * share their info between requests using the same context (i.e. they
1867     * keep multiple lists, one for each context).
1868     *
1869     * <P>The context may be any object. Contexts are considered equal
1870     * if <code>equals()</code> returns true. Examples of useful context
1871     * objects are threads (e.g. if you are running multiple clients, one
1872     * per thread) and sockets (e.g. if you are implementing a gateway).
1873     *
1874     * <P>When a new HTTPConnection is created it is initialized with a
1875     * default context which is the same for all instances. This method
1876     * must be invoked immediately after a new HTTPConnection is created
1877     * and before any request method is invoked. Furthermore, this method
1878     * may only be called once (i.e. the context is "sticky").
1879     *
1880     * @param context the new context; must be non-null
1881     * @exception IllegalArgumentException if <var>context</var> is null
1882     * @exception RuntimeException if the context has already been set
1883     */

1884    public void setContext(Object JavaDoc context)
1885    {
1886    if (context == null)
1887        throw new IllegalArgumentException JavaDoc("Context must be non-null");
1888    if (Context != null)
1889        throw new RuntimeException JavaDoc("Context already set");
1890
1891    Context = context;
1892    }
1893
1894
1895    /**
1896     * Returns the current context.
1897     *
1898     * @see #setContext(java.lang.Object)
1899     * @return the current context, or the default context if
1900     * <code>setContext()</code> hasn't been invoked
1901     */

1902    public Object JavaDoc getContext()
1903    {
1904    if (Context != null)
1905        return Context;
1906    else
1907        return dflt_context;
1908    }
1909
1910
1911    /**
1912     * Returns the default context.
1913     *
1914     * @see #setContext(java.lang.Object)
1915     * @return the default context
1916     */

1917    static Object JavaDoc getDefaultContext()
1918    {
1919    return dflt_context;
1920    }
1921
1922
1923    /**
1924     * Adds an authorization entry for the "digest" authorization scheme to
1925     * the list. If an entry already exists for the "digest" scheme and the
1926     * specified realm then it is overwritten.
1927     *
1928     * <P>This is a convenience method and just invokes the corresponding
1929     * method in AuthorizationInfo.
1930     *
1931     * @param realm the realm
1932     * @param user the username
1933     * @param passw the password
1934     * @see AuthorizationInfo#addDigestAuthorization(java.lang.String, int, java.lang.String, java.lang.String, java.lang.String)
1935     */

1936    public void addDigestAuthorization(String JavaDoc realm, String JavaDoc user, String JavaDoc passwd)
1937    {
1938    AuthorizationInfo.addDigestAuthorization(Host, Port, realm, user,
1939                         passwd, getContext());
1940    }
1941
1942
1943    /**
1944     * Adds an authorization entry for the "basic" authorization scheme to
1945     * the list. If an entry already exists for the "basic" scheme and the
1946     * specified realm then it is overwritten.
1947     *
1948     * <P>This is a convenience method and just invokes the corresponding
1949     * method in AuthorizationInfo.
1950     *
1951     * @param realm the realm
1952     * @param user the username
1953     * @param passw the password
1954     * @see AuthorizationInfo#addBasicAuthorization(java.lang.String, int, java.lang.String, java.lang.String, java.lang.String)
1955     */

1956    public void addBasicAuthorization(String JavaDoc realm, String JavaDoc user, String JavaDoc passwd)
1957    {
1958    AuthorizationInfo.addBasicAuthorization(Host, Port, realm, user,
1959                        passwd, getContext());
1960    }
1961
1962
1963    /**
1964     * Sets the default proxy server to use. The proxy will only be used
1965     * for new <var>HTTPConnection</var>s created after this call and will
1966     * not affect currrent instances of <var>HTTPConnection</var>. A null
1967     * or empty string <var>host</var> parameter disables the proxy.
1968     *
1969     * <P>In an application or using the Appletviewer an alternative to
1970     * this method is to set the following properties (either in the
1971     * properties file or on the command line):
1972     * <var>http.proxyHost</var> and <var>http.proxyPort</var>. Whether
1973     * <var>http.proxyHost</var> is set or not determines whether a proxy
1974     * server is used.
1975     *
1976     * <P>If the proxy server requires authorization and you wish to set
1977     * this authorization information in the code, then you may use any
1978     * of the <var>AuthorizationInfo.addXXXAuthorization()</var> methods to
1979     * do so. Specify the same <var>host</var> and <var>port</var> as in
1980     * this method. If you have not given any authorization info and the
1981     * proxy server requires authorization then you will be prompted for
1982     * the necessary info via a popup the first time you do a request.
1983     *
1984     * @see #setCurrentProxy(java.lang.String,int)
1985     * @param host the host on which the proxy server resides.
1986     * @param port the port the proxy server is listening on.
1987     */

1988    public static void setProxyServer(String JavaDoc host, int port)
1989    {
1990    if (host == null || host.trim().length() == 0)
1991        Default_Proxy_Host = null;
1992    else
1993    {
1994        Default_Proxy_Host = host.trim().toLowerCase();
1995        Default_Proxy_Port = port;
1996    }
1997    }
1998
1999
2000    /**
2001     * Sets the proxy used by this instance. This can be used to override
2002     * the proxy setting inherited from the default proxy setting. A null
2003     * or empty string <var>host</var> parameter disables the proxy.
2004     *
2005     * <P>Note that if you set a proxy for the connection using this
2006     * method, and a request made over this connection is redirected
2007     * to a different server, then the connection used for new server
2008     * will <em>not</em> pick this proxy setting, but instead will use
2009     * the default proxy settings.
2010     *
2011     * @see #setProxyServer(java.lang.String,int)
2012     * @param host the host the proxy runs on
2013     * @param port the port the proxy is listening on
2014     */

2015    public synchronized void setCurrentProxy(String JavaDoc host, int port)
2016    {
2017    if (host == null || host.trim().length() == 0)
2018        Proxy_Host = null;
2019    else
2020    {
2021        Proxy_Host = host.trim().toLowerCase();
2022        if (port <= 0)
2023        Proxy_Port = 80;
2024        else
2025        Proxy_Port = port;
2026    }
2027
2028    // the proxy might be talking a different version, so renegotiate
2029
switch(Protocol)
2030    {
2031        case HTTP:
2032        case HTTPS:
2033        if (force_1_0)
2034        {
2035            ServerProtocolVersion = HTTP_1_0;
2036            ServProtVersKnown = true;
2037            RequestProtocolVersion = "HTTP/1.0";
2038        }
2039        else
2040        {
2041            ServerProtocolVersion = HTTP_1_1;
2042            ServProtVersKnown = false;
2043            RequestProtocolVersion = "HTTP/1.1";
2044        }
2045        break;
2046        case HTTP_NG:
2047        ServerProtocolVersion = -1; /* Unknown */
2048        ServProtVersKnown = false;
2049        RequestProtocolVersion = "";
2050        break;
2051        case SHTTP:
2052        ServerProtocolVersion = -1; /* Unknown */
2053        ServProtVersKnown = false;
2054        RequestProtocolVersion = "Secure-HTTP/1.3";
2055        break;
2056        default:
2057        throw new Error JavaDoc("HTTPClient Internal Error: invalid protocol " +
2058                Protocol);
2059    }
2060
2061    KeepAliveUnknown = true;
2062    DoesKeepAlive = false;
2063
2064    input_demux = null;
2065    early_stall = null;
2066    late_stall = null;
2067    prev_resp = null;
2068    }
2069
2070
2071    /**
2072     * Add <var>host</var> to the list of hosts which should be accessed
2073     * directly, not via any proxy set by <code>setProxyServer()</code>.
2074     *
2075     * <P>The <var>host</var> may be any of:
2076     * <UL>
2077     * <LI>a complete host name (e.g. "www.disney.com")
2078     * <LI>a domain name; domain names must begin with a dot (e.g.
2079     * ".disney.com")
2080     * <LI>an IP-address (e.g. "12.34.56.78")
2081     * <LI>an IP-subnet, specified as an IP-address and a netmask separated
2082     * by a "/" (e.g. "34.56.78/255.255.255.192"); a 0 bit in the netmask
2083     * means that that bit won't be used in the comparison (i.e. the
2084     * addresses are AND'ed with the netmask before comparison).
2085     * </UL>
2086     *
2087     * <P>The two properties <var>HTTPClient.nonProxyHosts</var> and
2088     * <var>http.nonProxyHosts</var> are used when this class is loaded to
2089     * initialize the list of non-proxy hosts. The second property is only
2090     * read if the first one is not set; the second property is also used
2091     * the JDK's URLConnection. These properties must contain a "|"
2092     * separated list of entries which conform to the above rules for the
2093     * <var>host</var> parameter (e.g. "11.22.33.44|.disney.com").
2094     *
2095     * @param host a host name, domain name, IP-address or IP-subnet.
2096     * @exception ParseException if the length of the netmask does not match
2097     * the length of the IP-address
2098     */

2099    public static void dontProxyFor(String JavaDoc host) throws ParseException
2100    {
2101    host = host.trim().toLowerCase();
2102
2103    // check for domain name
2104

2105    if (host.charAt(0) == '.')
2106    {
2107        if (!non_proxy_dom_list.contains(host))
2108        non_proxy_dom_list.addElement(host);
2109        return;
2110    }
2111
2112
2113    // check for host name
2114

2115    for (int idx=0; idx<host.length(); idx++)
2116    {
2117        if (!Character.isDigit(host.charAt(idx)) &&
2118        host.charAt(idx) != '.' && host.charAt(idx) != '/')
2119        {
2120        non_proxy_host_list.put(host, "");
2121        return;
2122        }
2123    }
2124
2125
2126    // must be an IP-address
2127

2128    byte[] ip_addr;
2129    byte[] ip_mask;
2130    int slash;
2131    if ((slash = host.indexOf('/')) != -1) // IP subnet
2132
{
2133        ip_addr = string2arr(host.substring(0, slash));
2134        ip_mask = string2arr(host.substring(slash+1));
2135        if (ip_addr.length != ip_mask.length)
2136        throw new ParseException("length of IP-address (" +
2137                ip_addr.length + ") != length of netmask (" +
2138                ip_mask.length + ")");
2139    }
2140    else
2141    {
2142        ip_addr = string2arr(host);
2143        ip_mask = new byte[ip_addr.length];
2144        for (int idx=0; idx<ip_mask.length; idx++)
2145        ip_mask[idx] = (byte) 255;
2146    }
2147
2148
2149    // check if addr or subnet already exists
2150

2151    ip_loop: for (int idx=0; idx<non_proxy_addr_list.size(); idx++)
2152    {
2153        byte[] addr = (byte[]) non_proxy_addr_list.elementAt(idx);
2154        byte[] mask = (byte[]) non_proxy_mask_list.elementAt(idx);
2155        if (addr.length != ip_addr.length) continue;
2156
2157        for (int idx2=0; idx2<addr.length; idx2++)
2158        {
2159        if ((ip_addr[idx2] & mask[idx2]) != (addr[idx2] & mask[idx2]) ||
2160            (mask[idx2] != ip_mask[idx2]))
2161            continue ip_loop;
2162        }
2163
2164        return; // already exists
2165
}
2166    non_proxy_addr_list.addElement(ip_addr);
2167    non_proxy_mask_list.addElement(ip_mask);
2168    }
2169
2170
2171    /**
2172     * Convenience method to add a number of hosts at once. If any one
2173     * host is null or cannot be parsed it is ignored.
2174     *
2175     * @param hosts The list of hosts to set
2176     * @see #dontProxyFor(java.lang.String)
2177     * @since V0.3-2
2178     */

2179    public static void dontProxyFor(String JavaDoc[] hosts)
2180    {
2181        if (hosts == null || hosts.length == 0)
2182        return;
2183
2184        for (int idx=0; idx<hosts.length; idx++)
2185        {
2186            try
2187            {
2188                if (hosts[idx] != null)
2189                    dontProxyFor(hosts[idx]);
2190            }
2191            catch(ParseException pe)
2192            {
2193        // ignore it
2194
}
2195        }
2196    }
2197
2198
2199    /**
2200     * Remove <var>host</var> from the list of hosts for which the proxy
2201     * should not be used. The syntax for <var>host</var> is specified in
2202     * <code>dontProxyFor()</code>.
2203     *
2204     * @param host a host name, domain name, IP-address or IP-subnet.
2205     * @return true if the remove was sucessful, false otherwise
2206     * @exception ParseException if the length of the netmask does not match
2207     * the length of the IP-address
2208     * @see #dontProxyFor(java.lang.String)
2209     */

2210    public static boolean doProxyFor(String JavaDoc host) throws ParseException
2211    {
2212    host = host.trim().toLowerCase();
2213
2214    // check for domain name
2215

2216    if (host.charAt(0) == '.')
2217        return non_proxy_dom_list.removeElement(host);
2218
2219
2220    // check for host name
2221

2222    for (int idx=0; idx<host.length(); idx++)
2223    {
2224        if (!Character.isDigit(host.charAt(idx)) &&
2225        host.charAt(idx) != '.' && host.charAt(idx) != '/')
2226        return (non_proxy_host_list.remove(host) != null);
2227    }
2228
2229
2230    // must be an IP-address
2231

2232    byte[] ip_addr;
2233    byte[] ip_mask;
2234    int slash;
2235    if ((slash = host.indexOf('/')) != -1) // IP subnet
2236
{
2237        ip_addr = string2arr(host.substring(0, slash));
2238        ip_mask = string2arr(host.substring(slash+1));
2239        if (ip_addr.length != ip_mask.length)
2240        throw new ParseException("length of IP-address (" +
2241                ip_addr.length + ") != length of netmask (" +
2242                ip_mask.length + ")");
2243    }
2244    else
2245    {
2246        ip_addr = string2arr(host);
2247        ip_mask = new byte[ip_addr.length];
2248        for (int idx=0; idx<ip_mask.length; idx++)
2249        ip_mask[idx] = (byte) 255;
2250    }
2251
2252    ip_loop: for (int idx=0; idx<non_proxy_addr_list.size(); idx++)
2253    {
2254        byte[] addr = (byte[]) non_proxy_addr_list.elementAt(idx);
2255        byte[] mask = (byte[]) non_proxy_mask_list.elementAt(idx);
2256        if (addr.length != ip_addr.length) continue;
2257
2258        for (int idx2=0; idx2<addr.length; idx2++)
2259        {
2260        if ((ip_addr[idx2] & mask[idx2]) != (addr[idx2] & mask[idx2]) ||
2261            (mask[idx2] != ip_mask[idx2]))
2262            continue ip_loop;
2263        }
2264
2265        non_proxy_addr_list.removeElementAt(idx);
2266        non_proxy_mask_list.removeElementAt(idx);
2267        return true;
2268    }
2269    return false;
2270    }
2271
2272
2273    /**
2274     * Turn an IP-address string into an array (e.g. "12.34.56.78" into
2275     * { 12, 34, 56, 78 }).
2276     *
2277     * @param ip IP-address
2278     * @return IP-address in network byte order
2279     */

2280    private static byte[] string2arr(String JavaDoc ip)
2281    {
2282    byte[] arr;
2283    char[] ip_char = new char[ip.length()];
2284    ip.getChars(0, ip_char.length, ip_char, 0);
2285
2286    int cnt = 0;
2287    for (int idx=0; idx<ip_char.length; idx++)
2288        if (ip_char[idx] == '.') cnt++;
2289    arr = new byte[cnt+1];
2290
2291    cnt = 0;
2292    int pos = 0;
2293    for (int idx=0; idx<ip_char.length; idx++)
2294        if (ip_char[idx] == '.')
2295        {
2296        arr[cnt] = (byte) Integer.parseInt(ip.substring(pos, idx));
2297        cnt++;
2298        pos = idx+1;
2299        }
2300    arr[cnt] = (byte) Integer.parseInt(ip.substring(pos));
2301
2302    return arr;
2303    }
2304
2305
2306    /**
2307     * Sets the SOCKS server to use. The server will only be used
2308     * for new HTTPConnections created after this call and will not affect
2309     * currrent instances of HTTPConnection. A null or empty string host
2310     * parameter disables SOCKS.
2311     * <P>The code will try to determine the SOCKS version to use at
2312     * connection time. This might fail for a number of reasons, however,
2313     * in which case you must specify the version explicitly.
2314     *
2315     * @see #setSocksServer(java.lang.String, int, int)
2316     * @param host the host on which the proxy server resides. The port
2317     * used is the default port 1080.
2318     */

2319    public static void setSocksServer(String JavaDoc host)
2320    {
2321    setSocksServer(host, 1080);
2322    }
2323
2324
2325    /**
2326     * Sets the SOCKS server to use. The server will only be used
2327     * for new HTTPConnections created after this call and will not affect
2328     * currrent instances of HTTPConnection. A null or empty string host
2329     * parameter disables SOCKS.
2330     * <P>The code will try to determine the SOCKS version to use at
2331     * connection time. This might fail for a number of reasons, however,
2332     * in which case you must specify the version explicitly.
2333     *
2334     * @see #setSocksServer(java.lang.String, int, int)
2335     * @param host the host on which the proxy server resides.
2336     * @param port the port the proxy server is listening on.
2337     */

2338    public static void setSocksServer(String JavaDoc host, int port)
2339    {
2340    if (port <= 0)
2341        port = 1080;
2342
2343    if (host == null || host.length() == 0)
2344        Default_Socks_client = null;
2345    else
2346        Default_Socks_client = new SocksClient(host, port);
2347    }
2348
2349
2350    /**
2351     * Sets the SOCKS server to use. The server will only be used
2352     * for new HTTPConnections created after this call and will not affect
2353     * currrent instances of HTTPConnection. A null or empty string host
2354     * parameter disables SOCKS.
2355     *
2356     * <P>In an application or using the Appletviewer an alternative to
2357     * this method is to set the following properties (either in the
2358     * properties file or on the command line):
2359     * <var>HTTPClient.socksHost</var>, <var>HTTPClient.socksPort</var>
2360     * and <var>HTTPClient.socksVersion</var>. Whether
2361     * <var>HTTPClient.socksHost</var> is set or not determines whether a
2362     * SOCKS server is used; if <var>HTTPClient.socksPort</var> is not set
2363     * it defaults to 1080; if <var>HTTPClient.socksVersion</var> is not
2364     * set an attempt will be made to automatically determine the version
2365     * used by the server.
2366     *
2367     * <P>Note: If you have also set a proxy server then a connection
2368     * will be made to the SOCKS server, which in turn then makes a
2369     * connection to the proxy server (possibly via other SOCKS servers),
2370     * which in turn makes the final connection.
2371     *
2372     * <P>If the proxy server is running SOCKS version 5 and requires
2373     * username/password authorization, and you wish to set
2374     * this authorization information in the code, then you may use the
2375     * <var>AuthorizationInfo.addAuthorization()</var> method to do so.
2376     * Specify the same <var>host</var> and <var>port</var> as in this
2377     * method, give the <var>scheme</var> "SOCKS5" and the <var>realm</var>
2378     * "USER/PASS", set the <var>cookie</var> to null and the
2379     * <var>params</var> to an array containing a single <var>NVPair</var>
2380     * in turn containing the username and password. Example:
2381     * <PRE>
2382     * NVPair[] up = { new NVPair(username, password) };
2383     * AuthorizationInfo.addAuthorization(host, port, "SOCKS5", "USER/PASS",
2384     * null, up);
2385     * </PRE>
2386     * If you have not given any authorization info and the proxy server
2387     * requires authorization then you will be prompted for the necessary
2388     * info via a popup the first time you do a request.
2389     *
2390     * @param host the host on which the proxy server resides.
2391     * @param port the port the proxy server is listening on.
2392     * @param version the SOCKS version the server is running. Currently
2393     * this must be '4' or '5'.
2394     * @exception SocksException If <var>version</var> is not '4' or '5'.
2395     */

2396    public static void setSocksServer(String JavaDoc host, int port, int version)
2397        throws SocksException
2398    {
2399    if (port <= 0)
2400        port = 1080;
2401
2402    if (host == null || host.length() == 0)
2403        Default_Socks_client = null;
2404    else
2405        Default_Socks_client = new SocksClient(host, port, version);
2406    }
2407
2408
2409    /**
2410     * Removes the #... part. Returns the stripped name, or "" if either
2411     * the <var>file</var> is null or is the empty string (after stripping).
2412     *
2413     * @param file the name to strip
2414     * @return the stripped name
2415     */

2416    private final String JavaDoc stripRef(String JavaDoc file)
2417    {
2418    if (file == null) return "";
2419
2420    int hash = file.indexOf('#');
2421    if (hash != -1)
2422        file = file.substring(0,hash);
2423
2424    return file.trim();
2425    }
2426
2427
2428    // private helper methods
2429

2430    /**
2431     * Sets up the request, creating the list of headers to send and
2432     * creating instances of the modules.
2433     *
2434     * @param method GET, POST, etc.
2435     * @param resource the resource
2436     * @param headers an array of headers to be used
2437     * @param entity the entity (or null)
2438     * @return the response.
2439     * @exception java.io.IOException when an exception is returned from
2440     * the socket.
2441     * @exception ModuleException if an exception is encountered in any module.
2442     */

2443    private HTTPResponse setupRequest(String JavaDoc method, String JavaDoc resource,
2444                      NVPair[] headers, byte[] entity,
2445                      HttpOutputStream stream)
2446        throws IOException JavaDoc, ModuleException
2447    {
2448    Request req = new Request(this, method, resource,
2449                  mergedHeaders(headers), entity, stream,
2450                  AllowUI);
2451    RequestList.addToEnd(req);
2452
2453    try
2454    {
2455        HTTPResponse resp = new HTTPResponse(gen_mod_insts(), Timeout, req);
2456        handleRequest(req, resp, null, true);
2457        return resp;
2458    }
2459    finally
2460        { RequestList.remove(req); }
2461    }
2462
2463
2464    /**
2465     * This merges built-in default headers, user-specified default headers,
2466     * and method-specified headers. Method-specified take precedence over
2467     * user defaults, which take precedence over built-in defaults.
2468     *
2469     * The following headers are removed if found: "Host" and
2470     * "Content-length".
2471     *
2472     * @param spec the headers specified in the call to the method
2473     * @return an array consisting of merged headers.
2474     */

2475    private NVPair[] mergedHeaders(NVPair[] spec)
2476    {
2477    int spec_len = (spec != null ? spec.length : 0),
2478        defs_len;
2479    NVPair[] merged;
2480
2481    synchronized (DefaultHeaders)
2482    {
2483        defs_len = (DefaultHeaders != null ? DefaultHeaders.length : 0);
2484        merged = new NVPair[spec_len + defs_len];
2485
2486        // copy default headers
2487
System.arraycopy(DefaultHeaders, 0, merged, 0, defs_len);
2488    }
2489
2490    // merge in selected headers
2491
int sidx, didx = defs_len;
2492    for (sidx=0; sidx<spec_len; sidx++)
2493    {
2494        String JavaDoc s_name = spec[sidx].getName().trim();
2495        if (s_name.equalsIgnoreCase("Content-length") ||
2496        s_name.equalsIgnoreCase("Host"))
2497        continue;
2498
2499        int search;
2500        for (search=0; search<didx; search++)
2501        {
2502        if (merged[search].getName().trim().equalsIgnoreCase(s_name))
2503            break;
2504        }
2505
2506        merged[search] = spec[sidx];
2507        if (search == didx) didx++;
2508    }
2509
2510    if (didx < merged.length)
2511        merged = Util.resizeArray(merged, didx);
2512
2513    return merged;
2514    }
2515
2516
2517    /**
2518     * Generate an array of instances of the current modules.
2519     */

2520    private HTTPClientModule[] gen_mod_insts()
2521    {
2522    synchronized (ModuleList)
2523    {
2524        HTTPClientModule[] mod_insts =
2525        new HTTPClientModule[ModuleList.size()];
2526
2527        for (int idx=0; idx<ModuleList.size(); idx++)
2528        {
2529        Class JavaDoc mod = (Class JavaDoc) ModuleList.elementAt(idx);
2530        try
2531            { mod_insts[idx] = (HTTPClientModule) mod.newInstance(); }
2532        catch (Exception JavaDoc e)
2533        {
2534            throw new Error JavaDoc("HTTPClient Internal Error: could not " +
2535                    "create instance of " + mod.getName() +
2536                    " -\n" + e);
2537        }
2538        }
2539
2540        return mod_insts;
2541    }
2542    }
2543
2544
2545    /**
2546     * handles the Request. First the request handler for each module is
2547     * is invoked, and then if no response was generated the request is
2548     * sent.
2549     *
2550     * @param req the Request
2551     * @param http_resp the HTTPResponse
2552     * @param resp the Response
2553     * @param usemodules if false then skip module loop
2554     * @exception IOException if any module or sendRequest throws it
2555     * @exception ModuleException if any module throws it
2556     */

2557    void handleRequest(Request req, HTTPResponse http_resp, Response resp,
2558               boolean usemodules)
2559        throws IOException JavaDoc, ModuleException
2560    {
2561    Response[] rsp_arr = { resp };
2562    HTTPClientModule[] modules = http_resp.getModules();
2563
2564
2565    // invoke requestHandler for each module
2566

2567    if (usemodules)
2568    doModules: for (int idx=0; idx<modules.length; idx++)
2569    {
2570        int sts = modules[idx].requestHandler(req, rsp_arr);
2571        switch (sts)
2572        {
2573        case REQ_CONTINUE: // continue processing
2574
break;
2575
2576        case REQ_RESTART: // restart processing with first module
2577
idx = -1;
2578            continue doModules;
2579
2580        case REQ_SHORTCIRC: // stop processing and send
2581
break doModules;
2582
2583        case REQ_RESPONSE: // go to phase 2
2584
case REQ_RETURN: // return response immediately
2585
if (rsp_arr[0] == null)
2586            throw new Error JavaDoc("HTTPClient Internal Error: no " +
2587                    "response returned by module " +
2588                    modules[idx].getClass().getName());
2589            http_resp.set(req, rsp_arr[0]);
2590            if (req.getStream() != null)
2591            req.getStream().ignoreData(req);
2592            if (req.internal_subrequest) return;
2593            if (sts == REQ_RESPONSE)
2594            http_resp.handleResponse();
2595            else
2596            http_resp.init(rsp_arr[0]);
2597            return;
2598
2599        case REQ_NEWCON_RST: // new connection
2600
if (req.internal_subrequest) return;
2601            req.getConnection().
2602                handleRequest(req, http_resp, rsp_arr[0], true);
2603            return;
2604
2605        case REQ_NEWCON_SND: // new connection, send immediately
2606
if (req.internal_subrequest) return;
2607            req.getConnection().
2608                handleRequest(req, http_resp, rsp_arr[0], false);
2609            return;
2610
2611        default: // not valid
2612
throw new Error JavaDoc("HTTPClient Internal Error: invalid status"+
2613                    " " + sts + " returned by module " +
2614                    modules[idx].getClass().getName());
2615        }
2616    }
2617
2618    if (req.internal_subrequest) return;
2619
2620
2621    // Send the request across the wire
2622

2623    if (req.getStream() != null && req.getStream().getLength() == -1)
2624    {
2625        if (!ServProtVersKnown || ServerProtocolVersion < HTTP_1_1 ||
2626        no_chunked)
2627        {
2628        req.getStream().goAhead(req, null, http_resp.getTimeout());
2629        http_resp.set(req, req.getStream());
2630        }
2631        else
2632        {
2633        // add Transfer-Encoding header if necessary
2634
int idx;
2635        NVPair[] hdrs = req.getHeaders();
2636        for (idx=0; idx<hdrs.length; idx++)
2637            if (hdrs[idx].getName().equalsIgnoreCase("Transfer-Encoding"))
2638            break;
2639
2640        if (idx == hdrs.length)
2641        {
2642            hdrs = Util.resizeArray(hdrs, idx+1);
2643            hdrs[idx] = new NVPair("Transfer-Encoding", "chunked");
2644            req.setHeaders(hdrs);
2645        }
2646        else
2647        {
2648            String JavaDoc v = hdrs[idx].getValue();
2649            try
2650            {
2651            if (!Util.hasToken(v, "chunked"))
2652                hdrs[idx] = new NVPair("Transfer-Encoding",
2653                           v + ", chunked");
2654            }
2655            catch (ParseException pe)
2656            { throw new IOException JavaDoc(pe.toString()); }
2657        }
2658
2659        http_resp.set(req, sendRequest(req, http_resp.getTimeout()));
2660        }
2661    }
2662    else
2663        http_resp.set(req, sendRequest(req, http_resp.getTimeout()));
2664
2665    if (req.aborted) throw new IOException JavaDoc("Request aborted by user");
2666    }
2667
2668
2669    /** These mark the response to stall the next request on, if any */
2670    private Response early_stall = null;
2671    private Response late_stall = null;
2672    private Response prev_resp = null;
2673    /** This marks the socket output stream as still being used */
2674    private boolean output_finished = true;
2675
2676    /**
2677     * sends the request over the line.
2678     *
2679     * @param req the request
2680     * @param con_timeout the timeout to use when establishing a socket
2681     * connection; an InterruptedIOException is thrown
2682     * if the procedure times out.
2683     * @return the response.
2684     * @exception IOException if thrown by the socket
2685     * @exception InterruptedIOException if the connection is not established
2686     * within the specified timeout
2687     * @exception ModuleException if any module throws it during the SSL-
2688     * tunneling handshake
2689     */

2690    Response sendRequest(Request req, int con_timeout)
2691        throws IOException JavaDoc, ModuleException
2692    {
2693    ByteArrayOutputStream JavaDoc hdr_buf = new ByteArrayOutputStream JavaDoc(600);
2694    Response resp = null;
2695    boolean keep_alive;
2696
2697
2698    // The very first request is special in that we need its response
2699
// before any further requests may be made. This is to set things
2700
// like the server version.
2701

2702    if (early_stall != null)
2703    {
2704        try
2705        {
2706        if (DebugConn)
2707            System.err.println("Conn: Early-stalling Request: " +
2708                       req.getMethod() + " " +
2709                       req.getRequestURI());
2710
2711        synchronized(early_stall)
2712        {
2713            // wait till the response is received
2714
try
2715            { early_stall.getVersion(); }
2716            catch (IOException JavaDoc ioe)
2717            { }
2718            early_stall = null;
2719        }
2720        }
2721        catch (NullPointerException JavaDoc npe)
2722        { }
2723    }
2724
2725
2726    String JavaDoc[] con_hdrs = assembleHeaders(req, hdr_buf);
2727
2728
2729    // determine if the connection should be kept alive after this
2730
// request
2731

2732    try
2733    {
2734        if (ServerProtocolVersion >= HTTP_1_1 &&
2735         !Util.hasToken(con_hdrs[0], "close")
2736        ||
2737        ServerProtocolVersion == HTTP_1_0 &&
2738         Util.hasToken(con_hdrs[0], "keep-alive")
2739        )
2740        keep_alive = true;
2741        else
2742        keep_alive = false;
2743    }
2744    catch (ParseException pe)
2745        { throw new IOException JavaDoc(pe.toString()); }
2746
2747
2748    synchronized(this)
2749    {
2750    // Sometimes we must stall the pipeline until the previous request
2751
// has been answered. However, if we are going to open up a new
2752
// connection anyway we don't really need to stall.
2753

2754    if (late_stall != null)
2755    {
2756        if (input_demux != null || KeepAliveUnknown)
2757        {
2758        if (DebugConn)
2759            System.err.println("Conn: Stalling Request: " +
2760                   req.getMethod() + " " + req.getRequestURI());
2761
2762        try // wait till the response is received
2763
{
2764            late_stall.getVersion();
2765            if (KeepAliveUnknown)
2766            determineKeepAlive(late_stall);
2767        }
2768        catch (IOException JavaDoc ioe)
2769            { }
2770        }
2771
2772        late_stall = null;
2773    }
2774
2775
2776    /* POSTs must not be pipelined because of problems if the connection
2777     * is aborted. Since it is generally impossible to know what urls
2778     * POST will influence it is impossible to determine if a sequence
2779     * of requests containing a POST is idempotent.
2780     * Also, for retried requests we don't want to pipeline either.
2781     */

2782    if ((req.getMethod().equals("POST") || req.dont_pipeline) &&
2783        prev_resp != null && input_demux != null)
2784    {
2785        if (DebugConn)
2786        System.err.println("Conn: Stalling Request: " +
2787                   req.getMethod() + " " + req.getRequestURI());
2788
2789        try // wait till the response is received
2790
{ prev_resp.getVersion(); }
2791        catch (IOException JavaDoc ioe)
2792        { }
2793    }
2794
2795
2796    // If the previous request used an output stream, then wait till
2797
// all the data has been written
2798

2799    if (!output_finished)
2800    {
2801        try
2802        { wait(); }
2803        catch (InterruptedException JavaDoc ie)
2804        { throw new IOException JavaDoc(ie.toString()); }
2805    }
2806
2807
2808    if (req.aborted) throw new IOException JavaDoc("Request aborted by user");
2809
2810    int try_count = 3;
2811    /* what a hack! This is to handle the case where the server closes
2812     * the connection but we don't realize it until we try to send
2813     * something. The problem is that we only get IOException, but
2814     * we need a finer specification (i.e. whether it's an EPIPE or
2815     * something else); I don't trust relying on the message part
2816     * of IOException (which on SunOS/Solaris gives 'Broken pipe',
2817     * but what on Windoze/Mac?).
2818     */

2819
2820    while (try_count-- > 0)
2821    {
2822        try
2823        {
2824        // get a client socket
2825

2826        Socket JavaDoc sock;
2827        if (input_demux == null ||
2828            (sock = input_demux.getSocket()) == null)
2829        {
2830            sock = getSocket(con_timeout);
2831
2832            if (Protocol == HTTPS)
2833            {
2834            if (Proxy_Host != null)
2835            {
2836                Socket JavaDoc[] sarr = { sock };
2837                resp = enableSSLTunneling(sarr, req, con_timeout);
2838                if (resp != null)
2839                {
2840                resp.final_resp = true;
2841                return resp;
2842                }
2843                sock = sarr[0];
2844            }
2845
2846            //sock = new SSLSocket(sock);
2847
}
2848
2849            input_demux = new StreamDemultiplexor(Protocol, sock, this);
2850            DemuxList.addToEnd(input_demux);
2851            KeepAliveReqLeft = KeepAliveReqMax;
2852        }
2853
2854        if (req.aborted)
2855            throw new IOException JavaDoc("Request aborted by user");
2856
2857        if (DebugConn)
2858        {
2859            System.err.println("Conn: Sending Request: ");
2860            System.err.println();
2861            hdr_buf.writeTo(System.err);
2862        }
2863
2864
2865        // Send headers
2866

2867        OutputStream JavaDoc sock_out = sock.getOutputStream();
2868        if (haveMSLargeWritesBug)
2869            sock_out = new MSLargeWritesBugStream(sock_out);
2870
2871        hdr_buf.writeTo(sock_out);
2872
2873
2874        // Wait for "100 Continue" status if necessary
2875

2876        try
2877        {
2878            if (ServProtVersKnown &&
2879            ServerProtocolVersion >= HTTP_1_1 &&
2880            Util.hasToken(con_hdrs[1], "100-continue"))
2881            {
2882            resp = new Response(req, (Proxy_Host != null && Protocol != HTTPS), input_demux);
2883            resp.timeout = 60;
2884            if (resp.getContinue() != 100)
2885                break;
2886            }
2887        }
2888        catch (ParseException pe)
2889            { throw new IOException JavaDoc(pe.toString()); }
2890        catch (InterruptedIOException JavaDoc iioe)
2891            { }
2892        finally
2893            { if (resp != null) resp.timeout = 0; }
2894
2895
2896        // POST/PUT data
2897

2898        if (req.getData() != null && req.getData().length > 0)
2899        {
2900            if (req.delay_entity > 0)
2901            {
2902                        // wait for something on the network; check available()
2903
// roughly every 100 ms
2904

2905            long num_units = req.delay_entity / 100;
2906            long one_unit = req.delay_entity / num_units;
2907
2908                        for (int idx=0; idx<num_units; idx++)
2909                        {
2910                            if (input_demux.available(null) != 0)
2911                                break;
2912                            try { Thread.sleep(one_unit); }
2913                            catch (InterruptedException JavaDoc ie) { }
2914                        }
2915
2916                        if (input_demux.available(null) == 0)
2917                sock_out.write(req.getData()); // he's still waiting
2918
else
2919                keep_alive = false; // Uh oh!
2920
}
2921            else
2922            sock_out.write(req.getData());
2923        }
2924
2925        if (req.getStream() != null)
2926            req.getStream().goAhead(req, sock_out, 0);
2927        else
2928            sock_out.flush();
2929
2930
2931        // get a new response.
2932
// Note: this does not do a read on the socket.
2933

2934        if (resp == null)
2935            resp = new Response(req, (Proxy_Host != null &&
2936                         Protocol != HTTPS),
2937                    input_demux);
2938        }
2939        catch (IOException JavaDoc ioe)
2940        {
2941        if (DebugConn)
2942        {
2943            System.err.print("Conn: ");
2944            ioe.printStackTrace();
2945        }
2946
2947        closeDemux(ioe, true);
2948
2949        if (try_count == 0 || ioe instanceof UnknownHostException JavaDoc ||
2950            ioe instanceof InterruptedIOException JavaDoc || req.aborted)
2951            throw ioe;
2952
2953        if (DebugConn)
2954            System.err.println("Conn: Retrying request");
2955        continue;
2956        }
2957
2958        break;
2959    }
2960
2961    prev_resp = resp;
2962
2963
2964    // close the stream after this response if necessary
2965

2966    if ((!KeepAliveUnknown && !DoesKeepAlive) || !keep_alive ||
2967        (KeepAliveReqMax != -1 && KeepAliveReqLeft-- == 0))
2968    {
2969        input_demux.markForClose(resp);
2970        input_demux = null;
2971    }
2972    else
2973        input_demux.restartTimer();
2974
2975    if (DebugConn)
2976    {
2977        if (KeepAliveReqMax != -1)
2978        System.err.println("Conn: Number of requests left: "+
2979                    KeepAliveReqLeft);
2980    }
2981
2982
2983    /* We don't pipeline the first request, as we need some info
2984     * about the server (such as which http version it complies with)
2985     */

2986    if (!ServProtVersKnown)
2987        { early_stall = resp; resp.markAsFirstResponse(req); }
2988
2989    /* Also don't pipeline until we know if the server supports
2990     * keep-alive's or not.
2991     * Note: strictly speaking, HTTP/1.0 keep-alives don't mean we can
2992     * pipeline requests. I seem to remember some (beta?) version
2993     * of Netscape's Enterprise server which barfed if you tried
2994     * push requests down it's throat w/o waiting for the previous
2995     * response first. However, I've not been able to find such a
2996     * server lately, and so I'm taking the risk and assuming we
2997     * can in fact pipeline requests to HTTP/1.0 servers.
2998     */

2999    if (KeepAliveUnknown ||
3000        // We don't pipeline POST's ...
3001
!IdempotentSequence.methodIsIdempotent(req.getMethod()) ||
3002        req.dont_pipeline || // Retries disable pipelining too
3003
NeverPipeline) // Emergency measure: prevent all pipelining
3004
{ late_stall = resp; }
3005
3006
3007    /* If there is an output stream then just tell the other threads to
3008     * wait; the stream will notify() when it's done. If there isn't any
3009     * stream then wake up a waiting thread (if any).
3010     */

3011    if (req.getStream() != null)
3012        output_finished = false;
3013    else
3014    {
3015        output_finished = true;
3016        notify();
3017    }
3018
3019
3020    // Looks like were finally done
3021

3022    if (DebugConn) System.err.println("Conn: Request sent");
3023    }
3024
3025    return resp;
3026    }
3027
3028
3029    /**
3030     * Gets a socket. Creates a socket to the proxy if set, or else to the
3031     * actual destination.
3032     *
3033     * @param con_timeout if not 0 then start a new thread to establish the
3034     * the connection and join(con_timeout) it. If the
3035     * join() times out an InteruptedIOException is thrown.
3036     */

3037    private Socket JavaDoc getSocket(int con_timeout) throws IOException JavaDoc
3038    {
3039    Socket JavaDoc sock = null;
3040
3041    String JavaDoc actual_host;
3042    int actual_port;
3043
3044    if (Proxy_Host != null)
3045    {
3046        actual_host = Proxy_Host;
3047        actual_port = Proxy_Port;
3048    }
3049    else
3050    {
3051        actual_host = Host;
3052        actual_port = Port;
3053    }
3054
3055    if (DebugConn)
3056        System.err.println("Conn: Creating Socket: " + actual_host + ":" +
3057                actual_port);
3058
3059    if (con_timeout == 0) // normal connection establishment
3060
{
3061        if (Socks_client != null)
3062        sock = Socks_client.getSocket(actual_host, actual_port);
3063        else
3064        {
3065        // try all A records
3066
InetAddress JavaDoc[] addr_list = InetAddress.getAllByName(actual_host);
3067        for (int idx=0; idx<addr_list.length; idx++)
3068        {
3069            try
3070            {
3071            sock = new Socket JavaDoc(addr_list[idx], actual_port);
3072            break; // success
3073
}
3074            catch (SocketException JavaDoc se) // should be NoRouteToHostException
3075
{
3076            if (idx == addr_list.length-1)
3077                throw se; // we tried them all
3078
}
3079        }
3080        }
3081    }
3082    else
3083    {
3084        EstablishConnection con =
3085        new EstablishConnection(actual_host, actual_port, Socks_client);
3086        con.start();
3087        try
3088        { con.join((long) con_timeout); }
3089        catch (InterruptedException JavaDoc ie)
3090        { }
3091
3092        if (con.getException() != null)
3093        throw con.getException();
3094        if ((sock = con.getSocket()) == null)
3095        {
3096        con.forget();
3097        if ((sock = con.getSocket()) == null)
3098            throw new InterruptedIOException JavaDoc("Connection establishment timed out");
3099        }
3100    }
3101
3102    return sock;
3103    }
3104
3105
3106    /**
3107     * Enable SSL Tunneling if we're talking to a proxy. See ietf draft
3108     * draft-luotonen-ssl-tunneling-03 for more info.
3109     *
3110     * @param sock the socket
3111     * @param req the request initiating this connection
3112     * @param timeout the timeout
3113     * @return the proxy's last response if unsuccessful, or null if
3114     * tunnel successfuly established
3115     * @exception IOException
3116     * @exception ModuleException
3117     */

3118    private Response enableSSLTunneling(Socket JavaDoc[] sock, Request req, int timeout)
3119        throws IOException JavaDoc, ModuleException
3120    {
3121    // copy User-Agent and Proxy-Auth headers from request
3122

3123    Vector JavaDoc hdrs = new Vector JavaDoc();
3124    for (int idx=0; idx<req.getHeaders().length; idx++)
3125    {
3126        String JavaDoc name = req.getHeaders()[idx].getName();
3127        if (name.equalsIgnoreCase("User-Agent") ||
3128        name.equalsIgnoreCase("Proxy-Authorization"))
3129            hdrs.addElement(req.getHeaders()[idx]);
3130    }
3131
3132
3133    // create initial CONNECT subrequest
3134

3135    NVPair[] h = new NVPair[hdrs.size()];
3136    hdrs.copyInto(h);
3137    Request connect = new Request(this, "CONNECT", Host+":"+Port, h,
3138                      null, null, req.allowUI());
3139    connect.internal_subrequest = true;
3140
3141    ByteArrayOutputStream JavaDoc hdr_buf = new ByteArrayOutputStream JavaDoc(600);
3142    HTTPResponse r = new HTTPResponse(gen_mod_insts(), timeout, connect);
3143
3144
3145    // send and handle CONNECT request until successful or tired
3146

3147    Response resp = null;
3148
3149    while (true)
3150    {
3151        handleRequest(connect, r, resp, true);
3152
3153        hdr_buf.reset();
3154        assembleHeaders(connect, hdr_buf);
3155
3156        if (DebugConn)
3157        {
3158        System.err.println("Conn: Sending SSL-Tunneling Subrequest: ");
3159        System.err.println();
3160        hdr_buf.writeTo(System.err);
3161        }
3162
3163
3164        // send CONNECT
3165

3166        hdr_buf.writeTo(sock[0].getOutputStream());
3167
3168
3169        // return if successful
3170

3171        resp = new Response(connect, sock[0].getInputStream());
3172        if (resp.getStatusCode() == 200) return null;
3173
3174
3175        // failed!
3176

3177        // make life easy: read data and close socket
3178

3179        try
3180        { resp.getData(); }
3181        catch (IOException JavaDoc ioe)
3182        { }
3183        try
3184        { sock[0].close(); }
3185        catch (IOException JavaDoc ioe)
3186        { }
3187
3188
3189        // handle response
3190

3191        r.set(connect, resp);
3192        if (!r.handleResponse()) return resp;
3193
3194        sock[0] = getSocket(timeout);
3195    }
3196    }
3197
3198
3199    /**
3200     * This writes out the headers on the <var>hdr_buf</var>. It takes
3201     * special precautions for the following headers:
3202     * <DL>
3203     * <DT>Content-type<DI>This is only written if the request has an entity.
3204     * If the request has an entity and no content-type
3205     * header was given for the request it defaults to
3206     * "application/octet-stream"
3207     * <DT>Content-length<DI>This header is generated if the request has an
3208     * entity and the entity isn't being sent with the
3209     * Transfer-Encoding "chunked".
3210     * <DT>User-Agent <DI>If not present it will be generated with the current
3211     * HTTPClient version strings. Otherwise the version
3212     * string is appended to the given User-Agent string.
3213     * <DT>Connection <DI>This header is only written if no proxy is used.
3214     * If no connection header is specified and the server
3215     * is not known to understand HTTP/1.1 or later then
3216     * a "Connection: keep-alive" header is generated.
3217     * <DT>Proxy-Connection<DI>This header is only written if a proxy is used.
3218     * If no connection header is specified and the proxy
3219     * is not known to understand HTTP/1.1 or later then
3220     * a "Proxy-Connection: keep-alive" header is generated.
3221     * <DT>Keep-Alive <DI>This header is only written if the Connection or
3222     * Proxy-Connection header contains the Keep-Alive
3223     * token.
3224     * <DT>Expect <DI>If there is no entity and this header contains the
3225     * "100-continue" token then this token is removed.
3226     * before writing the header.
3227     * <DT>TE <DI>If this header does not exist, it is created; else
3228     * if the "trailers" token is not specified this token
3229     * is added; else the header is not touched.
3230     * </DL>
3231     *
3232     * Furthermore, it escapes various characters in request-URI.
3233     *
3234     * @param req the Request
3235     * @param hdr_buf the buffer onto which to write the headers
3236     * @return an array of headers; the first element contains the
3237     * the value of the Connection or Proxy-Connectin header,
3238     * the second element the value of the Expect header.
3239     * @exception IOException if writing on <var>hdr_buf</var> generates an
3240     * an IOException, or if an error occurs during
3241     * parsing of a header
3242     */

3243    private String JavaDoc[] assembleHeaders(Request req,
3244                     ByteArrayOutputStream JavaDoc hdr_buf)
3245        throws IOException JavaDoc
3246    {
3247    DataOutputStream JavaDoc dataout = new DataOutputStream JavaDoc(hdr_buf);
3248    String JavaDoc[] con_hdrs = { "", "" };
3249    NVPair[] hdrs = req.getHeaders();
3250
3251
3252
3253    // Generate request line and Host header
3254

3255    String JavaDoc file = Util.escapeUnsafeChars(req.getRequestURI());
3256    if (Proxy_Host != null && Protocol != HTTPS && !file.equals("*"))
3257        dataout.writeBytes(req.getMethod() + " http://" + Host + ":" + Port+
3258                   file + " " + RequestProtocolVersion + "\r\n");
3259    else
3260        dataout.writeBytes(req.getMethod() + " " + file + " " +
3261                   RequestProtocolVersion + "\r\n");
3262
3263    if (Port != 80)
3264        dataout.writeBytes("Host: " + Host + ":" + Port + "\r\n");
3265    else // Netscape-Enterprise has some bugs...
3266
dataout.writeBytes("Host: " + Host + "\r\n");
3267
3268
3269    // remember various headers
3270

3271    int ct_idx = -1,
3272        ua_idx = -1,
3273        co_idx = -1,
3274        pc_idx = -1,
3275        ka_idx = -1,
3276        ex_idx = -1,
3277        te_idx = -1,
3278        tc_idx = -1,
3279        ug_idx = -1;
3280    for (int idx=0; idx<hdrs.length; idx++)
3281    {
3282        String JavaDoc name = hdrs[idx].getName().trim();
3283        if (name.equalsIgnoreCase("Content-Type")) ct_idx = idx;
3284        else if (name.equalsIgnoreCase("User-Agent")) ua_idx = idx;
3285        else if (name.equalsIgnoreCase("Connection")) co_idx = idx;
3286        else if (name.equalsIgnoreCase("Proxy-Connection")) pc_idx = idx;
3287        else if (name.equalsIgnoreCase("Keep-Alive")) ka_idx = idx;
3288        else if (name.equalsIgnoreCase("Expect")) ex_idx = idx;
3289        else if (name.equalsIgnoreCase("TE")) te_idx = idx;
3290        else if (name.equalsIgnoreCase("Transfer-Encoding")) tc_idx = idx;
3291        else if (name.equalsIgnoreCase("Upgrade")) ug_idx = idx;
3292    }
3293
3294
3295    /*
3296     * What follows is the setup for persistent connections. We default
3297     * to doing persistent connections for both HTTP/1.0 and HTTP/1.1,
3298     * unless we're using a proxy server and HTTP/1.0 in which case we
3299     * must make sure we don't do persistence (because of the problem of
3300     * 1.0 proxies blindly passing the Connection header on).
3301     *
3302     * Note: there is a "Proxy-Connection" header for use with proxies.
3303     * This however is only understood by Netscape and Netapp caches.
3304     * Furthermore, it suffers from the same problem as the Connection
3305     * header in HTTP/1.0 except that at least two proxies must be
3306     * involved. But I've taken the risk now and decided to send the
3307     * Proxy-Connection header. If I get complaints I'll remove it again.
3308     *
3309     * In any case, with this header we can now modify the above to send
3310     * the Proxy-Connection header whenever we wouldn't send the normal
3311     * Connection header.
3312     */

3313
3314    String JavaDoc co_hdr = null;
3315    if (!(ServProtVersKnown && ServerProtocolVersion >= HTTP_1_1 &&
3316          co_idx == -1))
3317    {
3318        if (co_idx == -1)
3319        { // no connection header given by user
3320
co_hdr = "Keep-Alive";
3321        con_hdrs[0] = "Keep-Alive";
3322        }
3323        else
3324        {
3325        con_hdrs[0] = hdrs[co_idx].getValue().trim();
3326        co_hdr = con_hdrs[0];
3327        }
3328
3329        try
3330        {
3331        if (ka_idx != -1 &&
3332            Util.hasToken(con_hdrs[0], "keep-alive"))
3333            dataout.writeBytes("Keep-Alive: " +
3334                    hdrs[ka_idx].getValue().trim() + "\r\n");
3335        }
3336        catch (ParseException pe)
3337        {
3338        throw new IOException JavaDoc(pe.toString());
3339        }
3340    }
3341
3342    if ((Proxy_Host != null && Protocol != HTTPS) &&
3343        !(ServProtVersKnown && ServerProtocolVersion >= HTTP_1_1))
3344    {
3345        if (co_hdr != null)
3346        {
3347        dataout.writeBytes("Proxy-Connection: ");
3348        dataout.writeBytes(co_hdr);
3349        dataout.writeBytes("\r\n");
3350        co_hdr = null;
3351        }
3352    }
3353
3354    if (co_hdr != null)
3355    {
3356        try
3357        {
3358        if (!Util.hasToken(co_hdr, "TE"))
3359            co_hdr += ", TE";
3360        }
3361        catch (ParseException pe)
3362        { throw new IOException JavaDoc(pe.toString()); }
3363    }
3364    else
3365        co_hdr = "TE";
3366
3367    if (ug_idx != -1)
3368        co_hdr += ", Upgrade";
3369
3370    if (co_hdr != null)
3371    {
3372        dataout.writeBytes("Connection: ");
3373        dataout.writeBytes(co_hdr);
3374        dataout.writeBytes("\r\n");
3375    }
3376
3377
3378
3379    // handle TE header
3380

3381    if (te_idx != -1)
3382    {
3383        dataout.writeBytes("TE: ");
3384        Vector JavaDoc pte;
3385        try
3386        { pte = Util.parseHeader(hdrs[te_idx].getValue()); }
3387        catch (ParseException pe)
3388        { throw new IOException JavaDoc(pe.toString()); }
3389
3390        if (!pte.contains(new HttpHeaderElement("trailers")))
3391        dataout.writeBytes("trailers, ");
3392
3393        dataout.writeBytes(hdrs[te_idx].getValue().trim() + "\r\n");
3394    }
3395    else
3396        dataout.writeBytes("TE: trailers\r\n");
3397
3398
3399    // User-Agent
3400

3401    if (ua_idx != -1)
3402        dataout.writeBytes("User-Agent: " + hdrs[ua_idx].getValue().trim() + " "
3403                   + version + "\r\n");
3404    else
3405        dataout.writeBytes("User-Agent: " + version + "\r\n");
3406
3407
3408    // Write out any headers left
3409

3410    for (int idx=0; idx<hdrs.length; idx++)
3411    {
3412        if (idx != ct_idx && idx != ua_idx && idx != co_idx &&
3413        idx != pc_idx && idx != ka_idx && idx != ex_idx &&
3414        idx != te_idx)
3415        dataout.writeBytes(hdrs[idx].getName().trim() + ": " +
3416                   hdrs[idx].getValue().trim() + "\r\n");
3417    }
3418
3419
3420    // Handle Content-type, Content-length and Expect headers
3421

3422    if (req.getData() != null || req.getStream() != null)
3423    {
3424        dataout.writeBytes("Content-type: ");
3425        if (ct_idx != -1)
3426        dataout.writeBytes(hdrs[ct_idx].getValue().trim());
3427        else
3428        dataout.writeBytes("application/octet-stream");
3429        dataout.writeBytes("\r\n");
3430
3431        if (req.getData() != null)
3432        dataout.writeBytes("Content-length: " +req.getData().length +
3433                   "\r\n");
3434        else if (req.getStream().getLength() != -1 && tc_idx == -1)
3435        dataout.writeBytes("Content-length: " +
3436                   req.getStream().getLength() + "\r\n");
3437
3438        if (ex_idx != -1)
3439        {
3440        con_hdrs[1] = hdrs[ex_idx].getValue().trim();
3441        dataout.writeBytes("Expect: " + con_hdrs[1] + "\r\n");
3442        }
3443    }
3444    else if (ex_idx != -1)
3445    {
3446        Vector JavaDoc expect_tokens;
3447        try
3448        { expect_tokens = Util.parseHeader(hdrs[ex_idx].getValue()); }
3449        catch (ParseException pe)
3450        { throw new IOException JavaDoc(pe.toString()); }
3451
3452
3453        // remove any 100-continue tokens
3454

3455        HttpHeaderElement cont = new HttpHeaderElement("100-continue");
3456        while (expect_tokens.removeElement(cont)) ;
3457
3458
3459        // write out header if any tokens left
3460

3461        if (!expect_tokens.isEmpty())
3462        {
3463        con_hdrs[1] = Util.assembleHeader(expect_tokens);
3464        dataout.writeBytes("Expect: " + con_hdrs[1] + "\r\n");
3465        }
3466    }
3467
3468    dataout.writeBytes("\r\n"); // end of header
3469

3470    return con_hdrs;
3471    }
3472
3473
3474    /**
3475     * The very first request is special in that we use it to figure out the
3476     * protocol version the server (or proxy) is compliant with.
3477     *
3478     * @return true if all went fine, false if the request needs to be resent
3479     * @exception IOException if any exception is thrown by the response
3480     */

3481    boolean handleFirstRequest(Request req, Response resp) throws IOException JavaDoc
3482    {
3483    // read response headers to get protocol version used by
3484
// the server.
3485

3486    ServerProtocolVersion = String2ProtVers(resp.getVersion());
3487    ServProtVersKnown = true;
3488
3489    /* We need to treat connections through proxies specially, because
3490     * many HTTP/1.0 proxies do not downgrade an HTTP/1.1 response
3491     * version to HTTP/1.0 (i.e. when we are talking to an HTTP/1.1
3492     * server through an HTTP/1.0 proxy we are mislead to thinking we're
3493     * talking to an HTTP/1.1 proxy). We use the absence of the Via
3494     * header to detect whether we're talking to an HTTP/1.0 proxy.
3495     * However, this only works when the chain contains only HTTP/1.0
3496     * proxies; if you have <client - 1.0 proxy - 1.1 proxy - server>
3497     * then this will fail too. Unfortunately there seems to be no way
3498     * to reliably detect broken HTTP/1.0 proxies...
3499     */

3500    if ((Proxy_Host != null && Protocol != HTTPS) &&
3501        resp.getHeader("Via") == null)
3502        ServerProtocolVersion = HTTP_1_0;
3503
3504    if (DebugConn)
3505        System.err.println("Conn: Protocol Version established: " +
3506                   ProtVers2String(ServerProtocolVersion));
3507
3508
3509    // some (buggy) servers return an error status if they get a
3510
// version they don't comprehend
3511

3512    if (ServerProtocolVersion == HTTP_1_0 &&
3513        (resp.getStatusCode() == 400 || resp.getStatusCode() == 500))
3514    {
3515        input_demux.markForClose(resp);
3516        input_demux = null;
3517        RequestProtocolVersion = "HTTP/1.0";
3518        return false;
3519    }
3520
3521    return true;
3522    }
3523
3524
3525    private void determineKeepAlive(Response resp) throws IOException JavaDoc
3526    {
3527    // try and determine if this server does keep-alives
3528

3529    String JavaDoc con;
3530
3531    try
3532    {
3533        if (ServerProtocolVersion >= HTTP_1_1 ||
3534        (
3535         (
3536          ((Proxy_Host == null || Protocol == HTTPS) &&
3537           (con = resp.getHeader("Connection")) != null)
3538          ||
3539          ((Proxy_Host != null && Protocol != HTTPS) &&
3540           (con = resp.getHeader("Proxy-Connection")) != null)
3541         ) &&
3542         Util.hasToken(con, "keep-alive")
3543        )
3544           )
3545        {
3546        DoesKeepAlive = true;
3547
3548        if (DebugConn)
3549            System.err.println("Conn: Keep-Alive enabled");
3550
3551        KeepAliveUnknown = false;
3552        }
3553        else if (resp.getStatusCode() < 400)
3554        KeepAliveUnknown = false;
3555
3556
3557        // get maximum number of requests
3558

3559        if (DoesKeepAlive && ServerProtocolVersion == HTTP_1_0 &&
3560        (con = resp.getHeader("Keep-Alive")) != null)
3561        {
3562        HttpHeaderElement max =
3563                Util.getElement(Util.parseHeader(con), "max");
3564        if (max != null && max.getValue() != null)
3565        {
3566            KeepAliveReqMax = Integer.parseInt(max.getValue());
3567            KeepAliveReqLeft = KeepAliveReqMax;
3568
3569            if (DebugConn)
3570            System.err.println("Conn: Max Keep-Alive requests: "+
3571                       KeepAliveReqMax);
3572        }
3573        }
3574    }
3575    catch (ParseException pe) { }
3576    catch (NumberFormatException JavaDoc nfe) { }
3577    catch (ClassCastException JavaDoc cce) { }
3578    }
3579
3580
3581    synchronized void outputFinished()
3582    {
3583    output_finished = true;
3584    notify();
3585    }
3586
3587
3588    synchronized void closeDemux(IOException JavaDoc ioe, boolean was_reset)
3589    {
3590    if (input_demux != null) input_demux.close(ioe, was_reset);
3591
3592    early_stall = null;
3593    late_stall = null;
3594    prev_resp = null;
3595    }
3596
3597
3598    final static String JavaDoc ProtVers2String(int prot_vers)
3599    {
3600    return "HTTP/" + (prot_vers >>> 16) + "." + (prot_vers & 0xFFFF);
3601    }
3602
3603    final static int String2ProtVers(String JavaDoc prot_vers)
3604    {
3605    String JavaDoc vers = prot_vers.substring(5);
3606    int dot = vers.indexOf('.');
3607    return Integer.parseInt(vers.substring(0, dot)) << 16 |
3608        Integer.parseInt(vers.substring(dot+1));
3609    }
3610
3611
3612    /**
3613     * Generates a string of the form protocol://host.domain:port .
3614     *
3615     * @return the string
3616     */

3617    public String JavaDoc toString()
3618    {
3619    return getProtocol() + "://" + getHost() +
3620        (getPort() != URI.defaultPort(getProtocol()) ? ":" + getPort() : "");
3621    }
3622
3623
3624    private class EstablishConnection extends Thread JavaDoc
3625    {
3626    String JavaDoc actual_host;
3627    int actual_port;
3628    IOException JavaDoc exception;
3629    Socket JavaDoc sock;
3630    SocksClient Socks_client;
3631    boolean close;
3632
3633
3634    EstablishConnection(String JavaDoc host, int port, SocksClient socks)
3635    {
3636        super("EstablishConnection (" + host + ":" + port + ")");
3637        try { setDaemon(true); }
3638        catch (SecurityException JavaDoc se) { } // Oh well...
3639

3640        actual_host = host;
3641        actual_port = port;
3642        Socks_client = socks;
3643
3644        exception = null;
3645        sock = null;
3646        close = false;
3647    }
3648
3649
3650    public void run()
3651    {
3652        try
3653        {
3654        if (Socks_client != null)
3655            sock = Socks_client.getSocket(actual_host, actual_port);
3656        else
3657        {
3658            // try all A records
3659
InetAddress JavaDoc[] addr_list = InetAddress.getAllByName(actual_host);
3660            for (int idx=0; idx<addr_list.length; idx++)
3661            {
3662            try
3663            {
3664                sock = new Socket JavaDoc(addr_list[idx], actual_port);
3665                break; // success
3666
}
3667            catch (SocketException JavaDoc se) // should be NoRouteToHostException
3668
{
3669                if (idx == addr_list.length-1 || close)
3670                throw se; // we tried them all
3671
}
3672            }
3673        }
3674        }
3675        catch (IOException JavaDoc ioe)
3676        {
3677        exception = ioe;
3678        }
3679
3680        if (close && sock != null)
3681        {
3682        try
3683            { sock.close(); }
3684        catch (IOException JavaDoc ioe)
3685            { }
3686        sock = null;
3687        }
3688    }
3689
3690
3691    IOException JavaDoc getException()
3692    {
3693        return exception;
3694    }
3695
3696
3697    Socket JavaDoc getSocket()
3698    {
3699        return sock;
3700    }
3701
3702
3703    void forget()
3704    {
3705        close = true;
3706    }
3707    }
3708
3709
3710    /**
3711     * M$ has yet another bug in their WinSock: if you try to write too much
3712     * data at once it'll hang itself. This filter therefore splits big writes
3713     * up into multiple writes of at most 20K.
3714     */

3715    private class MSLargeWritesBugStream extends FilterOutputStream JavaDoc
3716    {
3717    private final int CHUNK_SIZE = 20000;
3718
3719    MSLargeWritesBugStream(OutputStream JavaDoc os)
3720    {
3721        super(os);
3722    }
3723
3724    public void write(byte[] b, int off, int len) throws IOException JavaDoc
3725    {
3726        while (len > CHUNK_SIZE)
3727        {
3728        out.write(b, off, CHUNK_SIZE);
3729        off += CHUNK_SIZE;
3730        len -= CHUNK_SIZE;
3731        }
3732        out.write(b, off, len);
3733    }
3734    }
3735}
3736
3737
Popular Tags