Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: HTTPClient/HTTPConnection.java


1   /*
2    * @(#)HTTPConnection.java        0.3-2E 13/11/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;
33  import java.io.DataOutputStream;
34  import java.io.FilterOutputStream;
35  import java.io.ByteArrayOutputStream;
36  import java.io.IOException;
37  import java.io.InterruptedIOException;
38  import java.net.URL;
39  import java.net.Socket;
40  import java.net.InetAddress;
41  import java.net.SocketException;
42  import java.net.UnknownHostException;
43  import java.util.Vector;
44  import java.util.Hashtable;
45  import java.applet.Applet;
46  
47  import javax.net.ssl.SSLSocket;
48  import javax.net.ssl.SSLException;
49  import javax.net.ssl.SSLSocketFactory;
50  import javax.security.cert.X509Certificate;
51  import com.sun.net.ssl.TrustManagerFactory;
52  import com.sun.net.ssl.TrustManager;
53  import java.security.NoSuchAlgorithmException;
54  import java.security.Provider;
55  import java.security.Security;
56  
57  
58  import java.io.FileInputStream;
59  import java.io.FileNotFoundException;
60  import com.sun.net.ssl.KeyManagerFactory;
61  import com.sun.net.ssl.SSLContext;
62  import java.security.KeyStore;
63  import java.security.KeyStoreException;
64  import java.security.NoSuchAlgorithmException;
65  import java.security.UnrecoverableKeyException;
66  import java.security.cert.CertificateException;
67  import java.security.KeyManagementException;
68  import HTTPClient.https.HttpsTestX509TrustManager;
69  
70  /**
71   * This class implements http protocol requests; it contains most of HTTP/1.1
72   * and ought to be unconditionally compliant.
73   * Redirections are automatically handled, and authorizations requests are
74   * recognized and dealt with via an authorization handler.
75   * Only full HTTP/1.0 and HTTP/1.1 requests are generated. HTTP/1.1, HTTP/1.0
76   * and HTTP/0.9 responses are recognized.
77   *
78   * <P>Using the HTTPClient should be quite simple. First add the import
79   * statement '<code>import HTTPClient.*;</code>' to your file(s). Request
80   * can then be sent using one of the methods <var>Head()</var>,
81   * <var>Get()</var>, <var>Post()</var>, etc in <var>HTTPConnection</var>.
82   * These methods all return an instance of <var>HTTPResponse</var> which
83   * has methods for accessing the response headers (<var>getHeader()</var>,
84   * <var>getHeaderAsInt()</var>, etc), various response info
85   * (<var>getStatusCode()</var>, <var>getReasonLine()</var>, etc) and the
86   * reponse data (<var>getData()</var> and <var>getInputStream()</var>).
87   * Following are some examples.
88   *
89   * <P>If this is in an applet you can retrieve files from your server
90   * as follows:
91   *
92   * <PRE>
93   *     try
94   *     {
95   *         HTTPConnection con = new HTTPConnection(this);
96   *         HTTPResponse   rsp = con.Get("/my_file");
97   *         if (rsp.getStatusCode() >= 300)
98   *         {
99   *             System.err.println("Received Error: "+rsp.getReasonLine());
100  *             System.err.println(new String(rsp.getData()));
101  *         }
102  *         else
103  *             data = rsp.getData();
104  *
105  *         rsp = con.Get("/another_file");
106  *         if (rsp.getStatusCode() >= 300)
107  *         {
108  *             System.err.println("Received Error: "+rsp.getReasonLine());
109  *             System.err.println(new String(rsp.getData()));
110  *         }
111  *         else
112  *             other_data = rsp.getData();
113  *     }
114  *     catch (IOException ioe)
115  *     {
116  *         System.err.println(ioe.toString());
117  *     }
118  *     catch (ModuleException me)
119  *     {
120  *         System.err.println("Error handling request: " + me.getMessage());
121  *     }
122  * </PRE>
123  *
124  * This will get the files "/my_file" and "/another_file" and put their
125  * contents into byte[]s accessible via <code>getData()</code>. Note that
126  * you need to only create a new <var>HTTPConnection</var> when sending a
127  * request to a new server (different host or port); although you may create
128  * a new <var>HTTPConnection</var> for every request to the same server this
129  * <strong>not</strong> recommended, as various information about the server
130  * is cached after the first request (to optimize subsequent requests) and
131  * persistent connections are used whenever possible.
132  *
133  * <P>To POST form data you would use something like this (assuming you
134  * have two fields called <var>name</var> and <var>e-mail</var>, whose
135  * contents are stored in the variables <var>name</var> and <var>email</var>):
136  *
137  * <PRE>
138  *     try
139  *     {
140  *         NVPair form_data[] = new NVPair[2];
141  *         form_data[0] = new NVPair("name", name);
142  *         form_data[1] = new NVPair("e-mail", email);
143  *
144  *         HTTPConnection con = new HTTPConnection(this);
145  *         HTTPResponse   rsp = con.Post("/cgi-bin/my_script", form_data);
146  *         if (rsp.getStatusCode() >= 300)
147  *         {
148  *             System.err.println("Received Error: "+rsp.getReasonLine());
149  *             System.err.println(new String(rsp.getData()));
150  *         }
151  *         else
152  *             stream = rsp.getInputStream();
153  *     }
154  *     catch (IOException ioe)
155  *     {
156  *         System.err.println(ioe.toString());
157  *     }
158  *     catch (ModuleException me)
159  *     {
160  *         System.err.println("Error handling request: " + me.getMessage());
161  *     }
162  * </PRE>
163  *
164  * Here the response data is read at leasure via an <var>InputStream</var>
165  * instead of all at once into a <var>byte[]</var>.
166  *
167  * <P>As another example, if you have a URL you're trying to send a request
168  * to you would do something like the following:
169  *
170  * <PRE>
171  *     try
172  *     {
173  *         URL url = new URL("http://www.mydomain.us/test/my_file");
174  *         HTTPConnection con = new HTTPConnection(url);
175  *         HTTPResponse   rsp = con.Put(url.getFile(), "Hello World");
176  *         if (rsp.getStatusCode() >= 300)
177  *         {
178  *             System.err.println("Received Error: "+rsp.getReasonLine());
179  *             System.err.println(new String(rsp.getData()));
180  *         }
181  *         else
182  *             data = rsp.getData();
183  *     }
184  *     catch (IOException ioe)
185  *     {
186  *         System.err.println(ioe.toString());
187  *     }
188  *     catch (ModuleException me)
189  *     {
190  *         System.err.println("Error handling request: " + me.getMessage());
191  *     }
192  * </PRE>
193  *
194  * <P>There are a whole number of methods for each request type; however the
195  * general forms are ([...] means that the enclosed is optional):
196  * <ul>
197  * <li> Head ( file [, form-data [, headers ] ] )
198  * <li> Head ( file [, query [, headers ] ] )
199  * <li> Get ( file [, form-data [, headers ] ] )
200  * <li> Get ( file [, query [, headers ] ] )
201  * <li> Post ( file [, form-data [, headers ] ] )
202  * <li> Post ( file [, data [, headers ] ] )
203  * <li> Post ( file [, stream [, headers ] ] )
204  * <li> Put ( file , data [, headers ] )
205  * <li> Put ( file , stream [, headers ] )
206  * <li> Delete ( file [, headers ] )
207  * <li> Options ( file [, headers [, data] ] )
208  * <li> Options ( file [, headers [, stream] ] )
209  * <li> Trace ( file [, headers ] )
210  * </ul>
211  *
212  * @version  0.3-2E  13/11/1999
213  * @author  Ronald Tschalär
214  */
215 
216 public class HTTPConnection
217   implements GlobalConstants, HTTPClientModuleConstants
218 {
219 
220     /**
221      *  Variable that hold SSL information and their setter methods
222      **/
223     protected static String keyStorePassword = null;
224     protected static String keyStoreLocation = null;
225 
226     public static synchronized void setKeyStorePassword(String passwd) {
227   if (keyStorePassword != null) {
228       return;
229   }
230   keyStorePassword = passwd;
231     }
232 
233     public static synchronized void setKeyStoreLocation(String loc) {
234   if (keyStoreLocation != null) {
235       return;
236   }
237   keyStoreLocation = loc;
238     }
239 
240     protected static SSLContext sslContext = null;
241 
242     /**
243      * Method to add custom trust manager that mimics browser behavior to
244      * allow untrusted peers.  This method must be called after keystore 
245      * password and keystore location is set.
246      **/
247     public static synchronized void addCustomX509TrustManager() {
248   if (sslContext != null) {
249       return;
250   }
251   KeyManagerFactory kmf = null;
252   KeyStore ks = null;
253   char[] passphrase = keyStorePassword.toCharArray();
254 
255   try {
256       sslContext = SSLContext.getInstance("SSLv3");
257   } catch (NoSuchAlgorithmException e) {
258       System.err.println("NoSuchAlgorithmException caught while getting SSLContext: " + e);
259   }
260 
261   try {
262       kmf = KeyManagerFactory.getInstance("SunX509");
263   } catch (NoSuchAlgorithmException e) {
264       System.err.println("NoSuchAlgorithmException caucht while getting  KeyManagerFactory instance: " + e);
265   }
266 
267   try {      
268       ks = KeyStore.getInstance("JKS");
269   } catch (KeyStoreException e) {
270       System.err.println("KeyStoreException caucht while getting KeyStore instance: " + e);
271   }
272 
273   try {
274       ks.load(new FileInputStream(keyStoreLocation), passphrase);
275   } catch (NoSuchAlgorithmException e) {
276       System.err.println("NoSuchAlgorithmException caucht on KeyStore load: " + e);
277   } catch (CertificateException e) {
278       System.err.println("CertificateException caucht on KeyStore load: " + e);
279   } catch (FileNotFoundException e) {
280       System.err.println("FileNotFoundException caucht on KeyStore load: " + e);
281   } catch (IOException e) {
282       System.err.println("IOException caucht on KeyStore load: " + e);
283   }
284 
285   try {
286       kmf.init(ks, passphrase);
287   } catch (KeyStoreException e) {
288       System.err.println("KeyStoreException caucht on KeyManagerFactory init: " + e);
289   } catch (NoSuchAlgorithmException e) {
290       System.err.println("NoSuchAlgorithmException caucht on KeyManagerFactory init: " + e);
291   } catch (UnrecoverableKeyException e) {
292       System.err.println("NoSuchAlgorithmException caucht on KeyManagerFactory init: " + e);
293   }
294 
295   try {
296       sslContext.init(kmf.getKeyManagers(),
297        HttpsTestX509TrustManager.getTrustManagers("SunX509", 
298                     ks),
299           null);
300   } catch (KeyManagementException e) {
301       System.err.println("KeyManagementException caucht on SSLContext init: " + e);
302   } catch (KeyStoreException e) {
303       System.err.println("KeyStoreException caucht on SSLContext init: " + e);
304   } catch (FileNotFoundException e) {
305       System.err.println("FileNotFoundException caucht on SSLContext init: " + e);
306   } catch (IOException e) {
307       System.err.println("IOException caucht on SSLContext init: " + e);
308   } catch (CertificateException e) {
309       System.err.println("CertificateException caucht on SSLContext init: " + e);
310   } catch (NoSuchAlgorithmException e) {
311       System.err.println("NoSuchAlgorithmException caucht on SSLContext init: " + e);
312   }
313     }
314  
315     /** The current version of this package. */
316     public final static String   version = "RPT-HTTPClient/0.3-2E";
317 
318     /** The default context */
319     private final static Object  dflt_context = new Object();
320 
321     /** The current context */
322     private Object               Context = null;
323 
324     /** The protocol used on this connection */
325     private int                  Protocol;
326 
327     /** The server's protocol version; M.m stored as (M<<16 | m) */
328             int        ServerProtocolVersion;
329 
330     /** Have we gotten the server's protocol version yet? */
331             boolean     ServProtVersKnown;
332 
333     /** The protocol version we send in a request; this is always HTTP/1.1
334   unless we're talking to a broken server in which case it's HTTP/1.0 */
335     private String     RequestProtocolVersion;
336 
337     /** hack to force buffering of data instead of using chunked T-E */
338     private static boolean       no_chunked = false;
339 
340     /** hack to force HTTP/1.0 requests */
341     private static boolean       force_1_0 = false;
342 
343     /** The remote host this connection is associated with */
344     private String               Host;
345 
346     /** The remote port this connection is attached to */
347     private int                  Port;
348 
349     /** The current proxy host to use (if any) */
350     private String               Proxy_Host = null;
351 
352     /** The current proxy port */
353     private int                  Proxy_Port;
354 
355     /** The default proxy host to use (if any) */
356     private static String        Default_Proxy_Host = null;
357 
358     /** The default proxy port */
359     private static int           Default_Proxy_Port;
360 
361     /** The list of hosts for which no proxy is to be used */
362     private static CIHashtable   non_proxy_host_list = new CIHashtable();
363     private static Vector        non_proxy_dom_list  = new Vector();
364     private static Vector        non_proxy_addr_list = new Vector();
365     private static Vector        non_proxy_mask_list = new Vector();
366 
367     /** The socks server to use */
368     private SocksClient          Socks_client = null;
369 
370     /** The default socks server to use */
371     private static SocksClient   Default_Socks_client = null;
372 
373     /** the current stream demultiplexor */
374     private StreamDemultiplexor  input_demux = null;
375 
376     /** a list of active stream demultiplexors */
377       LinkedList           DemuxList = new LinkedList();
378 
379     /** a list of active requests */
380     private LinkedList           RequestList = new LinkedList();
381 
382     /** does the server support keep-alive's? */
383     private boolean              DoesKeepAlive = false;
384 
385     /** have we been able to determine the above yet? */
386     private boolean              KeepAliveUnknown = true;
387 
388     /** the maximum number of requests over a HTTP/1.0 keep-alive connection */
389     private int                  KeepAliveReqMax = -1;
390 
391     /** the number of requests over a HTTP/1.0 keep-alive connection left */
392     private int                  KeepAliveReqLeft;
393 
394     /** hack to be able to disable pipelining */
395     private static boolean       NeverPipeline = false;
396 
397     /** hack to be able to disable keep-alives */
398     private static boolean       NoKeepAlives = false;
399 
400     /** hack to work around M$ bug */
401     private static boolean       haveMSLargeWritesBug = false;
402 
403     /** the default timeout to use for new connections */
404     private static int           DefaultTimeout = 0;
405 
406     /** the timeout to use for reading responses */
407     private int                   Timeout;
408 
409     /** The list of default http headers */
410     private NVPair[]             DefaultHeaders = new NVPair[0];
411 
412     /** The default list of modules (as a Vector of Class objects) */
413     private static Vector        DefaultModuleList;
414 
415     /** The list of modules (as a Vector of Class objects) */
416     private Vector               ModuleList;
417 
418     /** controls whether modules are allowed to interact with user */
419     private static boolean       DefaultAllowUI = true;
420 
421     /** controls whether modules are allowed to interact with user */
422     private boolean              AllowUI;
423 
424 
425     static
426     {
427   /*
428    * Let's try and see if we can figure out whether any proxies are
429    * being used.
430    */
431 
432   try    // JDK 1.1 naming
433   {
434       String host = System.getProperty("http.proxyHost");
435       if (host == null)
436     throw new Exception();    // try JDK 1.0.x naming
437       int port = Integer.getInteger("http.proxyPort", -1).intValue();
438 
439       if (DebugConn)
440     System.err.println("Conn:  using proxy " + host + ":" + port);
441       setProxyServer(host, port);
442   }
443   catch (Exception e)
444   {
445       try    // JDK 1.0.x naming
446       {
447     if (Boolean.getBoolean("proxySet"))
448     {
449         String host = System.getProperty("proxyHost");
450         int    port = Integer.getInteger("proxyPort", -1).intValue();
451         if (DebugConn)
452       System.err.println("Conn:  using proxy " + host + ":" + port);
453         setProxyServer(host, port);
454     }
455       }
456       catch (Exception ee)
457     { Default_Proxy_Host = null; }
458   }
459 
460 
461   /*
462    * now check for the non-proxy list
463    */
464   try
465   {
466       String hosts = System.getProperty("HTTPClient.nonProxyHosts");
467       if (hosts == null)
468     hosts = System.getProperty("http.nonProxyHosts");
469 
470       String[] list = Util.splitProperty(hosts);
471       dontProxyFor(list);
472   }
473   catch (Exception e)
474       { }
475 
476 
477   /*
478    * we can't turn the JDK SOCKS handling off, so we don't use the
479    * properties 'socksProxyHost' and 'socksProxyPort'. Instead we
480    * define 'HTTPClient.socksHost', 'HTTPClient.socksPort' and
481    * 'HTTPClient.socksVersion'.
482    */
483   try
484   {
485       String host = System.getProperty("HTTPClient.socksHost");
486       if (host != null  &&  host.length() > 0)
487       {
488     int port    = Integer.getInteger("HTTPClient.socksPort", -1).intValue();
489     int version = Integer.getInteger("HTTPClient.socksVersion", -1).intValue();
490     if (DebugConn)
491         System.err.println("Conn:  using SOCKS " + host + ":" + port);
492     if (version == -1)
493         setSocksServer(host, port);
494     else
495         setSocksServer(host, port, version);
496       }
497   }
498   catch (Exception e)
499       { Default_Socks_client = null; }
500 
501 
502   // Set up module list
503 
504   String modules = "HTTPClient.RetryModule|" +
505        "HTTPClient.CookieModule|" +
506        "HTTPClient.RedirectionModule|" +
507        "HTTPClient.AuthorizationModule|" +
508        "HTTPClient.DefaultModule|" +
509        "HTTPClient.TransferEncodingModule|" +
510        "HTTPClient.ContentMD5Module|" +
511        "HTTPClient.ContentEncodingModule";
512 
513   boolean in_applet = false;
514   try
515       { modules = System.getProperty("HTTPClient.Modules", modules); }
516   catch (SecurityException se)
517       { in_applet = true; }
518 
519   DefaultModuleList = new Vector();
520   String[] list     = Util.splitProperty(modules);
521   for (int idx=0; idx<list.length; idx++)
522   {
523       try
524       {
525     DefaultModuleList.addElement(Class.forName(list[idx]));
526     if (DebugConn)
527         System.err.println("Conn:  added module " + list[idx]);
528       }
529       catch (ClassNotFoundException cnfe)
530       {
531     if (!in_applet)
532         throw new NoClassDefFoundError(cnfe.getMessage());
533 
534     /* Just ignore it. This allows for example applets to just
535      * load the necessary modules - if you don't need a module
536      * then don't provide it, and it won't be added to the
537      * list. The disadvantage is that if you accidently misstype
538      * a module name this will lead to a "silent" error.
539      */
540       }
541   }
542 
543 
544   /*
545    * Hack: disable pipelining
546    */
547   try
548   {
549       NeverPipeline = Boolean.getBoolean("HTTPClient.disable_pipelining");
550       if (DebugConn)
551     if (NeverPipeline)  System.err.println("Conn:  disabling pipelining");
552   }
553   catch (Exception e)
554       { }
555 
556   /*
557    * Hack: disable keep-alives
558    */
559   try
560   {
561       NoKeepAlives = Boolean.getBoolean("HTTPClient.disableKeepAlives");
562       if (DebugConn)
563     if (NoKeepAlives)  System.err.println("Conn:  disabling keep-alives");
564   }
565   catch (Exception e)
566       { }
567 
568   /*
569    * Hack: force HTTP/1.0 requests
570    */
571   try
572   {
573       force_1_0 = Boolean.getBoolean("HTTPClient.forceHTTP_1.0");
574       if (DebugConn)
575     if (force_1_0)  System.err.println("Conn:  forcing HTTP/1.0 requests");
576   }
577   catch (Exception e)
578       { }
579 
580   /*
581    * Hack: prevent chunking of request data
582    */
583   try
584   {
585       no_chunked = Boolean.getBoolean("HTTPClient.dontChunkRequests");
586       if (DebugConn)
587     if (no_chunked)  System.err.println("Conn:  never chunking requests");
588   }
589   catch (Exception e)
590       { }
591 
592   /*
593    * M$ bug: large writes hang the stuff
594    */
595   try
596   {
597       if (System.getProperty("os.name").indexOf("Windows") >= 0  &&
598     System.getProperty("java.version").startsWith("1.1"))
599         haveMSLargeWritesBug = true;
600       if (DebugConn)
601     if (haveMSLargeWritesBug)
602         System.err.println("Conn:  splitting large writes into 20K chunks (M$ bug)");
603   }
604   catch (Exception e)
605       { }
606     }
607 
608 
609     // Constructors
610 
611     /**
612      * Constructs a connection to the host from where the applet was loaded.
613      * Note that current security policies only let applets connect home.
614      *
615      * @param applet the current applet
616      */
617     public HTTPConnection(Applet applet)  throws ProtocolNotSuppException
618     {
619   this(applet.getCodeBase().getProtocol(),
620        applet.getCodeBase().getHost(),
621        applet.getCodeBase().getPort());
622     }
623 
624     /**
625      * Constructs a connection to the specified host on port 80
626      *
627      * @param host the host
628      */
629     public HTTPConnection(String host)
630     {
631   Setup(HTTP, host, 80);
632     }
633 
634     /**
635      * Constructs a connection to the specified host on the specified port
636      *
637      * @param host the host
638      * @param port the port
639      */
640     public HTTPConnection(String host, int port)
641     {
642   Setup(HTTP, host, port);
643     }
644 
645     /**
646      * Constructs a connection to the specified host on the specified port,
647      * using the specified protocol (currently only "http" is supported).
648      *
649      * @param prot the protocol
650      * @param host the host
651      * @param port the port, or -1 for the default port
652      * @exception ProtocolNotSuppException if the protocol is not HTTP
653      */
654     public HTTPConnection(String prot, String host, int port)  throws
655   ProtocolNotSuppException
656     {
657   prot = prot.trim().toLowerCase();
658 
659   if (!prot.equals("http")  &&  !prot.equals("https"))
660       throw new ProtocolNotSuppException("Unsupported protocol '" + prot + "'");
661 
662   if (prot.equals("http"))
663       Setup(HTTP, host, port);
664   else if (prot.equals("https"))
665       Setup(HTTPS, host, port);
666   else if (prot.equals("shttp"))
667       Setup(SHTTP, host, port);
668   else if (prot.equals("http-ng"))
669       Setup(HTTP_NG, host, port);
670     }
671 
672     /**
673      * Constructs a connection to the host (port) as given in the url.
674      *
675      * @param     url the url
676      * @exception ProtocolNotSuppException if the protocol is not HTTP
677      */
678     public HTTPConnection(URL url) throws ProtocolNotSuppException
679     {
680   this(url.getProtocol(), url.getHost(), url.getPort());
681     }
682 
683     /**
684      * Sets the class variables. Must not be public.
685      *
686      * @param prot the protocol
687      * @param host the host
688      * @param port the port
689      */
690     private void Setup(int prot, String host, int port)
691     {
692   Protocol = prot;
693   Host     = host.trim().toLowerCase();
694   Port     = port;
695 
696   if (Port == -1)
697       Port = URI.defaultPort(getProtocol());
698 
699   if (Default_Proxy_Host != null  &&  !matchNonProxy(Host))
700       setCurrentProxy(Default_Proxy_Host, Default_Proxy_Port);
701   else
702       setCurrentProxy(null, 0);
703 
704   Socks_client = Default_Socks_client;
705   Timeout      = DefaultTimeout;
706   ModuleList   = (Vector) DefaultModuleList.clone();
707   AllowUI      = DefaultAllowUI;
708   if (NoKeepAlives)
709       setDefaultHeaders(new NVPair[] { new NVPair("Connection", "close") });
710     }
711 
712 
713     /**
714      * Determines if the given host matches any entry in the non-proxy list.
715      *
716      * @param host the host to match - must be trim()'d and lowercase
717      * @return true if a match is found, false otherwise
718      * @see #dontProxyFor(java.lang.String)
719      */
720     private boolean matchNonProxy(String host)
721     {
722   // Check host name list
723 
724   if (non_proxy_host_list.get(host) != null)
725       return true;
726 
727 
728   // Check domain name list
729 
730   for (int idx=0; idx<non_proxy_dom_list.size(); idx++)
731       if (host.endsWith((String) non_proxy_dom_list.elementAt(idx)))
732     return true;
733 
734 
735   // Check IP-address and subnet list
736 
737   if (non_proxy_addr_list.size() == 0)
738       return false;
739 
740   InetAddress[] host_addr;
741   try
742       { host_addr = InetAddress.getAllByName(host); }
743   catch (UnknownHostException uhe)
744       { return false; }  // maybe the proxy has better luck
745 
746   for (int idx=0; idx<non_proxy_addr_list.size(); idx++)
747   {
748       byte[] addr = (byte[]) non_proxy_addr_list.elementAt(idx);
749       byte[] mask = (byte[]) non_proxy_mask_list.elementAt(idx);
750 
751       ip_loop: for (int idx2=0; idx2<host_addr.length; idx2++)
752       {
753     byte[] raw_addr = host_addr[idx2].getAddress();
754     if (raw_addr.length != addr.length)  continue;
755 
756     for (int idx3=0; idx3<raw_addr.length; idx3++)
757     {
758         if ((raw_addr[idx3] & mask[idx3]) != (addr[idx3] & mask[idx3]))
759       continue ip_loop;
760     }
761     return true;
762       }
763   }
764 
765   return false;
766     }
767 
768 
769     // Methods
770 
771     /**
772      * Sends the HEAD request. This request is just like the corresponding
773      * GET except that it only returns the headers and no data.
774      *
775      * @see #Get(java.lang.String)
776      * @param     file the absolute path of the file
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 Head(String file)  throws IOException, ModuleException
783     {
784   return Head(file, (String) null, null);
785     }
786 
787     /**
788      * Sends the HEAD request. This request is just like the corresponding
789      * GET except that it only returns the headers and no data.
790      *
791      * @see #Get(java.lang.String, HTTPClient.NVPair[])
792      * @param     file      the absolute path of the file
793      * @param     form_data an array of Name/Value pairs
794      * @return    an HTTPResponse structure containing the response
795      * @exception java.io.IOException when an exception is returned from
796      *                                the socket.
797      * @exception ModuleException if an exception is encountered in any module.
798      */
799     public HTTPResponse Head(String file, NVPair form_data[])
800     throws IOException, ModuleException
801     {
802   return Head(file, form_data, null);
803     }
804 
805     /**
806      * Sends the HEAD request. This request is just like the corresponding
807      * GET except that it only returns the headers and no data.
808      *
809      * @see #Get(java.lang.String, HTTPClient.NVPair[], HTTPClient.NVPair[])
810      * @param     file      the absolute path of the file
811      * @param     form_data an array of Name/Value pairs
812      * @param     headers   additional headers
813      * @return    an HTTPResponse structure containing the response
814      * @exception java.io.IOException when an exception is returned from
815      *                                the socket.
816      * @exception ModuleException if an exception is encountered in any module.
817      */
818     public HTTPResponse Head(String file, NVPair[] form_data, NVPair[] headers)
819     throws IOException, ModuleException
820     {
821   String File  = stripRef(file),
822          query = Codecs.nv2query(form_data);
823   if (query != null  &&  query.length() > 0)
824       File += "?" + query;
825 
826   return setupRequest("HEAD", File, headers, null, null);
827     }
828 
829     /**
830      * Sends the HEAD request. This request is just like the corresponding
831      * GET except that it only returns the headers and no data.
832      *
833      * @see #Get(java.lang.String, java.lang.String)
834      * @param     file   the absolute path of the file
835      * @param     query   the query string; it will be urlencoded
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 Head(String file, String query)
842     throws IOException, ModuleException
843     {
844   return Head(file, query, null);
845     }
846 
847 
848     /**
849      * Sends the HEAD request. This request is just like the corresponding
850      * GET except that it only returns the headers and no data.
851      *
852      * @see #Get(java.lang.String, java.lang.String, HTTPClient.NVPair[])
853      * @param     file    the absolute path of the file
854      * @param     query   the query string; it will be urlencoded
855      * @param     headers additional headers
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 Head(String file, String query, NVPair[] headers)
862     throws IOException, ModuleException
863     {
864   String File = stripRef(file);
865   if (query != null  &&  query.length() > 0)
866       File += "?" + Codecs.URLEncode(query);
867 
868   return setupRequest("HEAD", File, headers, null, null);
869     }
870 
871 
872     /**
873      * GETs the file.
874      *
875      * @param     file the absolute path of the file
876      * @return    an HTTPResponse structure containing the response
877      * @exception java.io.IOException when an exception is returned from
878      *                                the socket.
879      * @exception ModuleException if an exception is encountered in any module.
880      */
881     public HTTPResponse Get(String file)  throws IOException, ModuleException
882     {
883   return Get(file, (String) null, null);
884     }
885 
886     /**
887      * GETs the file with a query consisting of the specified form-data.
888      * The data is urlencoded, turned into a string of the form
889      * "name1=value1&name2=value2" and then sent as a query string.
890      *
891      * @param     file       the absolute path of the file
892      * @param     form_data  an array of Name/Value pairs
893      * @return    an HTTPResponse structure containing the response
894      * @exception java.io.IOException when an exception is returned from
895      *                                the socket.
896      * @exception ModuleException if an exception is encountered in any module.
897      */
898     public HTTPResponse Get(String file, NVPair form_data[])
899     throws IOException, ModuleException
900     {
901   return Get(file, form_data, null);
902     }
903 
904     /**
905      * GETs the file with a query consisting of the specified form-data.
906      * The data is urlencoded, turned into a string of the form
907      * "name1=value1&name2=value2" and then sent as a query string.
908      *
909      * @param     file       the absolute path of the file
910      * @param     form_data  an array of Name/Value pairs
911      * @param     headers    additional headers
912      * @return    an HTTPResponse structure containing the response
913      * @exception java.io.IOException when an exception is returned from
914      *                                the socket.
915      * @exception ModuleException if an exception is encountered in any module.
916      */
917     public HTTPResponse Get(String file, NVPair[] form_data, NVPair[] headers)
918     throws IOException, ModuleException
919     {
920   String File  = stripRef(file),
921          query = Codecs.nv2query(form_data);
922   if (query != null  &&  query.length() > 0)
923       File += "?" + query;
924 
925   return setupRequest("GET", File, headers, null, null);
926     }
927 
928     /**
929      * GETs the file using the specified query string. The query string
930      * is first urlencoded.
931      *
932      * @param     file  the absolute path of the file
933      * @param     query the query
934      * @return    an HTTPResponse structure containing the response
935      * @exception java.io.IOException when an exception is returned from
936      *                                the socket.
937      * @exception ModuleException if an exception is encountered in any module.
938      */
939     public HTTPResponse Get(String file, String query)
940     throws IOException, ModuleException
941     {
942   return Get(file, query, null);
943     }
944 
945     /**
946      * GETs the file using the specified query string. The query string
947      * is first urlencoded.
948      *
949      * @param     file     the absolute path of the file
950      * @param     query    the query string
951      * @param     headers  additional headers
952      * @return    an HTTPResponse structure containing the response
953      * @exception java.io.IOException when an exception is returned from
954      *                                the socket.
955      * @exception ModuleException if an exception is encountered in any module.
956      */
957     public HTTPResponse Get(String file, String query, NVPair[] headers)
958     throws IOException, ModuleException
959     {
960   String File = stripRef(file);
961   if (query != null  &&  query.length() > 0)
962       File += "?" + Codecs.URLEncode(query);
963 
964   return setupRequest("GET", File, headers, null, null);
965     }
966 
967 
968     /**
969      * POSTs to the specified file. No data is sent.
970      *
971      * @param     file the absolute path of the file
972      * @return    an HTTPResponse structure containing the response
973      * @exception java.io.IOException when an exception is returned from
974      *                                the socket.
975      * @exception ModuleException if an exception is encountered in any module.
976      */
977     public HTTPResponse Post(String file)  throws IOException, ModuleException
978     {
979   return Post(file, (byte []) null, null);
980     }
981 
982     /**
983      * POSTs form-data to the specified file. The data is first urlencoded
984      * and then turned into a string of the form "name1=value1&name2=value2".
985      * A <var>Content-type</var> header with the value
986      * <var>application/x-www-form-urlencoded</var> is added.
987      *
988      * @param     file      the absolute path of the file
989      * @param     form_data an array of Name/Value pairs
990      * @return    an HTTPResponse structure containing the response
991      * @exception java.io.IOException when an exception is returned from
992      *                                the socket.
993      * @exception ModuleException if an exception is encountered in any module.
994      */
995     public HTTPResponse Post(String file, NVPair form_data[])
996     throws IOException, ModuleException
997     {
998   NVPair[] headers =
999       { new NVPair("Content-type", "application/x-www-form-urlencoded") };
1000
1001  return Post(file, Codecs.nv2query(form_data), headers);
1002    }
1003
1004    /**
1005     * POST's form-data to the specified file using the specified headers.
1006     * The data is first urlencoded and then turned into a string of the
1007     * form "name1=value1&name2=value2". If no <var>Content-type</var> header
1008     * is given then one is added with a value of
1009     * <var>application/x-www-form-urlencoded</var>.
1010     *
1011     * @param     file      the absolute path of the file
1012     * @param     form_data an array of Name/Value pairs
1013     * @param     headers   additional headers
1014     * @return    a HTTPResponse structure containing the response
1015     * @exception java.io.IOException when an exception is returned from
1016     *                                the socket.
1017     * @exception ModuleException if an exception is encountered in any module.
1018     */
1019    public HTTPResponse Post(String file, NVPair form_data[], NVPair headers[])
1020                throws IOException, ModuleException
1021    {
1022  int idx;
1023  for (idx=0; idx<headers.length; idx++)
1024      if (headers[idx].getName().equalsIgnoreCase("Content-type")) break;
1025  if (idx == headers.length)
1026  {
1027      headers = Util.resizeArray(headers, idx+1);
1028      headers[idx] =
1029    new NVPair("Content-type", "application/x-www-form-urlencoded");
1030  }
1031
1032  return Post(file, Codecs.nv2query(form_data), headers);
1033    }
1034
1035    /**
1036     * POSTs the data to the specified file. The data is converted to an
1037     * array of bytes using the lower byte of each character.
1038     * The request is sent using the content-type "application/octet-stream".
1039     *
1040     * @see java.lang.String#getBytes(int, int, byte[], int)
1041     * @param     file the absolute path of the file
1042     * @param     data the data
1043     * @return    an HTTPResponse structure containing the response
1044     * @exception java.io.IOException when an exception is returned from
1045     *                                the socket.
1046     * @exception ModuleException if an exception is encountered in any module.
1047     */
1048    public HTTPResponse Post(String file, String data)
1049    throws IOException, ModuleException
1050    {
1051  return Post(file, data, null);
1052    }
1053
1054    /**
1055     * POSTs the data to the specified file using the specified headers.
1056     *
1057     * @param     file     the absolute path of the file
1058     * @param     data     the data
1059     * @param     headers  additional headers
1060     * @return    an HTTPResponse structure containing the response
1061     * @exception java.io.IOException when an exception is returned from
1062     *                                the socket.
1063     * @exception ModuleException if an exception is encountered in any module.
1064     */
1065    public HTTPResponse Post(String file, String data, NVPair[] headers)
1066    throws IOException, ModuleException
1067    {
1068  byte tmp[] = null;
1069
1070  if (data != null  &&  data.length() > 0)
1071  {
1072      tmp = new byte[data.length()];
1073      data.getBytes(0, data.length(), tmp, 0);
1074  }
1075
1076  return Post(file, tmp, headers);
1077    }
1078
1079    /**
1080     * POSTs the raw data to the specified file.
1081     * The request is sent using the content-type "application/octet-stream"
1082     *
1083     * @param     file the absolute path of the file
1084     * @param     data the data
1085     * @return    an HTTPResponse structure containing the response
1086     * @exception java.io.IOException when an exception is returned from
1087     *                                the socket.
1088     * @exception ModuleException if an exception is encountered in any module.
1089     */
1090    public HTTPResponse Post(String file, byte data[])
1091    throws IOException, ModuleException
1092    {
1093  return Post(file, data, null);
1094    }
1095
1096    /**
1097     * POSTs the raw data to the specified file using the specified headers.
1098     *
1099     * @param     file     the absolute path of the file
1100     * @param     data     the data
1101     * @param     headers  additional headers
1102     * @return    an HTTPResponse structure containing the response
1103     * @exception java.io.IOException when an exception is returned from
1104     *                                the socket.
1105     * @exception ModuleException if an exception is encountered in any module.
1106     */
1107    public HTTPResponse Post(String file, byte data[], NVPair[] headers)
1108    throws IOException, ModuleException
1109    {
1110  if (data == null)  data = new byte[0];  // POST must always have a CL
1111  return setupRequest("POST", stripRef(file), headers, data, null);
1112    }
1113
1114
1115    /**
1116     * POSTs the data written to the output stream to the specified file.
1117     * The request is sent using the content-type "application/octet-stream"
1118     *
1119     * @param     file   the absolute path of the file
1120     * @param     stream the output stream on which the data is written
1121     * @return    an HTTPResponse structure containing the response
1122     * @exception java.io.IOException when an exception is returned from
1123     *                                the socket.
1124     * @exception ModuleException if an exception is encountered in any module.
1125     */
1126    public HTTPResponse Post(String file, HttpOutputStream stream)
1127    throws IOException, ModuleException
1128    {
1129  return Post(file, stream, null);
1130    }
1131
1132    /**
1133     * POSTs the data written to the output stream to the specified file
1134     * using the specified headers.
1135     *
1136     * @param     file     the absolute path of the file
1137     * @param     stream   the output stream on which the data is written
1138     * @param     headers  additional headers
1139     * @return    an HTTPResponse structure containing the response
1140     * @exception java.io.IOException when an exception is returned from
1141     *                                the socket.
1142     * @exception ModuleException if an exception is encountered in any module.
1143     */
1144    public HTTPResponse Post(String file, HttpOutputStream stream,
1145           NVPair[] headers)
1146    throws IOException, ModuleException
1147    {
1148  return setupRequest("POST", stripRef(file), headers, null, stream);
1149    }
1150
1151
1152    /**
1153     * PUTs the data into the specified file. The data is converted to an
1154     * array of bytes using the lower byte of each character.
1155     * The request ist sent using the content-type "application/octet-stream".
1156     *
1157     * @see java.lang.String#getBytes(int, int, byte[], int)
1158     * @param     file the absolute path of the file
1159     * @param     data the data
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 Put(String file, String data)
1166    throws IOException, ModuleException
1167    {
1168  return Put(file, data, null);
1169    }
1170
1171    /**
1172     * PUTs the data into the specified file using the additional headers
1173     * for the request.
1174     *
1175     * @param     file     the absolute path of the file
1176     * @param     data     the data
1177     * @param     headers  additional headers
1178     * @return    an HTTPResponse structure containing the response
1179     * @exception java.io.IOException when an exception is returned from
1180     *                                the socket.
1181     * @exception ModuleException if an exception is encountered in any module.
1182     */
1183    public HTTPResponse Put(String file, String data, NVPair[] headers)
1184    throws IOException, ModuleException
1185    {
1186  byte tmp[] = null;
1187
1188  if (data != null)
1189  {
1190      tmp = new byte[data.length()];
1191      data.getBytes(0, data.length(), tmp, 0);
1192  }
1193
1194  return Put(file, tmp, headers);
1195    }
1196
1197    /**
1198     * PUTs the raw data into the specified file.
1199     * The request is sent using the content-type "application/octet-stream".
1200     *
1201     * @param     file     the absolute path of the file
1202     * @param     data     the data
1203     * @return    an HTTPResponse structure containing the response
1204     * @exception java.io.IOException when an exception is returned from
1205     *                                the socket.
1206     * @exception ModuleException if an exception is encountered in any module.
1207     */
1208    public HTTPResponse Put(String file, byte data[])
1209    throws IOException, ModuleException
1210    {
1211  return Put(file, data, null);
1212    }
1213
1214    /**
1215     * PUTs the raw data into the specified file using the additional
1216     * headers.
1217     *
1218     * @param     file     the absolute path of the file
1219     * @param     data     the data
1220     * @param     headers  any additional headers
1221     * @return    an HTTPResponse structure containing the response
1222     * @exception java.io.IOException when an exception is returned from
1223     *                                the socket.
1224     * @exception ModuleException if an exception is encountered in any module.
1225     */
1226    public HTTPResponse Put(String file, byte data[], NVPair[] headers)
1227    throws IOException, ModuleException
1228    {
1229  if (data == null)  data = new byte[0];  // PUT must always have a CL
1230  return setupRequest("PUT", stripRef(file), headers, data, null);
1231    }
1232
1233    /**
1234     * PUTs the data written to the output stream into the specified file.
1235     * The request is sent using the content-type "application/octet-stream".
1236     *
1237     * @param     file     the absolute path of the file
1238     * @param     stream   the output stream on which the data is written
1239     * @return    an HTTPResponse structure containing the response
1240     * @exception java.io.IOException when an exception is returned from
1241     *                                the socket.
1242     * @exception ModuleException if an exception is encountered in any module.
1243     */
1244    public HTTPResponse Put(String file, HttpOutputStream stream)
1245    throws IOException, ModuleException
1246    {
1247  return Put(file, stream, null);
1248    }
1249
1250    /**
1251     * PUTs the data written to the output stream into the specified file
1252     * using the additional headers.
1253     *
1254     * @param     file     the absolute path of the file
1255     * @param     stream   the output stream on which the data is written
1256     * @param     headers  any additional headers
1257     * @return    an HTTPResponse structure containing the response
1258     * @exception java.io.IOException when an exception is returned from
1259     *                                the socket.
1260     * @exception ModuleException if an exception is encountered in any module.
1261     */
1262    public HTTPResponse Put(String file, HttpOutputStream stream,
1263          NVPair[] headers)
1264    throws IOException, ModuleException
1265    {
1266  return setupRequest("PUT", stripRef(file), headers, null, stream);
1267    }
1268
1269
1270    /**
1271     * Request OPTIONS from the server. If <var>file</var> is "*" then
1272     * the request applies to the server as a whole; otherwise it applies
1273     * only to that resource.
1274     *
1275     * @param     file     the absolute path of the resource, or "*"
1276     * @return    an HTTPResponse structure containing the response
1277     * @exception java.io.IOException when an exception is returned from
1278     *                                the socket.
1279     * @exception ModuleException if an exception is encountered in any module.
1280     */
1281    public HTTPResponse Options(String file)
1282    throws IOException, ModuleException
1283    {
1284  return Options(file, null, (byte[]) null);
1285    }
1286
1287
1288    /**
1289     * Request OPTIONS from the server. If <var>file</var> is "*" then
1290     * the request applies to the server as a whole; otherwise it applies
1291     * only to that resource.
1292     *
1293     * @param     file     the absolute path of the resource, or "*"
1294     * @param     headers  the headers containing optional info.
1295     * @return    an HTTPResponse structure containing the response
1296     * @exception java.io.IOException when an exception is returned from
1297     *                                the socket.
1298     * @exception ModuleException if an exception is encountered in any module.
1299     */
1300    public HTTPResponse Options(String file, NVPair[] headers)
1301    throws IOException, ModuleException
1302    {
1303  return Options(file, headers, (byte[]) null);
1304    }
1305
1306
1307    /**
1308     * Request OPTIONS from the server. If <var>file</var> is "*" then
1309     * the request applies to the server as a whole; otherwise it applies
1310     * only to that resource.
1311     *
1312     * @param     file     the absolute path of the resource, or "*"
1313     * @param     headers  the headers containing optional info.
1314     * @param     data     any data to be sent in the optional body
1315     * @return    an HTTPResponse structure containing the response
1316     * @exception java.io.IOException when an exception is returned from
1317     *                                the socket.
1318     * @exception ModuleException if an exception is encountered in any module.
1319     */
1320    public HTTPResponse Options(String file, NVPair[] headers, byte[] data)
1321    throws IOException, ModuleException
1322    {
1323  return setupRequest("OPTIONS", stripRef(file), headers, data, null);
1324    }
1325
1326
1327    /**
1328     * Request OPTIONS from the server. If <var>file</var> is "*" then
1329     * the request applies to the server as a whole; otherwise it applies
1330     * only to that resource.
1331     *
1332     * @param     file     the absolute path of the resource, or "*"
1333     * @param     headers  the headers containing optional info.
1334     * @param     stream   an output stream for sending the optional body
1335     * @return    an HTTPResponse structure containing the response
1336     * @exception java.io.IOException when an exception is returned from
1337     *                                the socket.
1338     * @exception ModuleException if an exception is encountered in any module.
1339     */
1340    public HTTPResponse Options(String file, NVPair[] headers,
1341        HttpOutputStream stream)
1342    throws IOException, ModuleException
1343    {
1344  return setupRequest("OPTIONS", stripRef(file), headers, null, stream);
1345    }
1346
1347
1348    /**
1349     * Requests that <var>file</var> be DELETEd from the server.
1350     *
1351     * @param     file     the absolute path of the resource
1352     * @return    an HTTPResponse structure containing the response
1353     * @exception java.io.IOException when an exception is returned from
1354     *                                the socket.
1355     * @exception ModuleException if an exception is encountered in any module.
1356     */
1357    public HTTPResponse Delete(String file)
1358    throws IOException, ModuleException
1359    {
1360  return Delete(file, null);
1361    }
1362
1363
1364    /**
1365     * Requests that <var>file</var> be DELETEd from the server.
1366     *
1367     * @param     file     the absolute path of the resource
1368     * @param     headers  additional headers
1369     * @return    an HTTPResponse structure containing the response
1370     * @exception java.io.IOException when an exception is returned from
1371     *                                the socket.
1372     * @exception ModuleException if an exception is encountered in any module.
1373     */
1374    public HTTPResponse Delete(String file, NVPair[] headers)
1375    throws IOException, ModuleException
1376    {
1377  return setupRequest("DELETE", stripRef(file), headers, null, null);
1378    }
1379
1380
1381    /**
1382     * Requests a TRACE. Headers of particular interest here are "Via"
1383     * and "Max-Forwards".
1384     *
1385     * @param     file     the absolute path of the resource
1386     * @param     headers  additional headers
1387     * @return    an HTTPResponse structure containing the response
1388     * @exception java.io.IOException when an exception is returned from
1389     *                                the socket.
1390     * @exception ModuleException if an exception is encountered in any module.
1391     */
1392    public HTTPResponse Trace(String file, NVPair[] headers)
1393    throws IOException, ModuleException
1394    {
1395  return setupRequest("TRACE", stripRef(file), headers, null, null);
1396    }
1397
1398
1399    /**
1400     * Requests a TRACE.
1401     *
1402     * @param     file     the absolute path of the resource
1403     * @return    an HTTPResponse structure containing the response
1404     * @exception java.io.IOException when an exception is returned from
1405     *                                the socket.
1406     * @exception ModuleException if an exception is encountered in any module.
1407     */
1408    public HTTPResponse Trace(String file)
1409    throws IOException, ModuleException
1410    {
1411  return Trace(file, null);
1412    }
1413
1414
1415    /**
1416     * This is here to allow an arbitrary, non-standard request to be sent.
1417     * I'm assuming you know what you are doing...
1418     *
1419     * @param     method   the extension method
1420     * @param     file     the absolute path of the resource, or null
1421     * @param     data     optional data, or null
1422     * @param     headers  optional headers, or null
1423     * @return    an HTTPResponse structure containing the response
1424     * @exception java.io.IOException when an exception is returned from
1425     *                                the socket.
1426     * @exception ModuleException if an exception is encountered in any module.
1427     */
1428    public HTTPResponse ExtensionMethod(String method, String file,
1429          byte[] data, NVPair[] headers)
1430    throws IOException, ModuleException
1431    {
1432  return setupRequest(method.trim(), stripRef(file), headers, data, null);
1433    }
1434
1435
1436    /**
1437     * This is here to allow an arbitrary, non-standard request to be sent.
1438     * I'm assuming you know what you are doing...
1439     *
1440     * @param     method   the extension method
1441     * @param     file     the absolute path of the resource, or null
1442     * @param     stream   optional output stream, or null
1443     * @param     headers  optional headers, or null
1444     * @return    an HTTPResponse structure containing the response
1445     * @exception java.io.IOException when an exception is returned from
1446     *                                the socket.
1447     * @exception ModuleException if an exception is encountered in any module.
1448     */
1449    public HTTPResponse ExtensionMethod(String method, String file,
1450          HttpOutputStream os, NVPair[] headers)
1451    throws IOException, ModuleException
1452    {
1453  return setupRequest(method.trim(), stripRef(file), headers, null, os);
1454    }
1455
1456
1457    /**
1458     * Aborts all the requests currently in progress on this connection and
1459     * closes all associated sockets.
1460     *
1461     * <P>Note: there is a small window where a request method such as
1462     * <code>Get()</code> may have been invoked but the request has not
1463     * been built and added to the list. Any request in this window will
1464     * not be aborted.
1465     *
1466     * @since V0.2-3
1467     */
1468    public void stop()
1469    {
1470  for (Request req = (Request) RequestList.enumerate(); req != null;
1471       req = (Request) RequestList.next())
1472      req.aborted = true;
1473
1474  for (StreamDemultiplexor demux =
1475        (StreamDemultiplexor) DemuxList.enumerate();
1476       demux != null; demux = (StreamDemultiplexor) DemuxList.next())
1477      demux.abort();
1478    }
1479
1480
1481    /**
1482     * Sets the default http headers to be sent with each request. The
1483     * actual headers sent are determined as follows: for each header
1484     * specified in multiple places a value given as part of the request
1485     * takes priority over any default values set by this method, which
1486     * in turn takes priority over any built-in default values. A different
1487     * way of looking at it is that we start off with a list of all headers
1488     * specified with the request, then add any default headers set by this
1489     * method which aren't already in our list, and finally add any built-in
1490     * headers which aren't yet in the list. There are two exceptions to this
1491     * rule: "Content-length" and "Host" headers are always ignored; and when
1492     * posting form-data any default "Content-type" is ignored in favor of
1493     * the built-in "application/x-www-form-urlencoded" (however it will be
1494     * overriden by any content-type header specified as part of the request).
1495     *
1496     * <P>Typical headers you might want to set here are "Accept" and its
1497     * "Accept-*" relatives, "Connection", "From", "User-Agent", etc.
1498     *
1499     * @param headers an array of header-name/value pairs (do not give the
1500     *                separating ':').
1501     */
1502    public void setDefaultHeaders(NVPair[] headers)
1503    {
1504  int length = (headers == null ? 0 : headers.length);
1505  NVPair[] def_hdrs = new NVPair[length];
1506
1507  // weed out undesired headers
1508  int sidx, didx;
1509  for (sidx=0, didx=0; sidx<length; sidx++)
1510  {
1511      String name = headers[sidx].getName().trim();
1512      if (name.equalsIgnoreCase("Content-length") ||
1513    name.equalsIgnoreCase("Host"))
1514    continue;
1515
1516      def_hdrs[didx++] = headers[sidx];
1517  }
1518
1519  if (didx < length)
1520      def_hdrs = Util.resizeArray(DefaultHeaders, didx);
1521
1522  synchronized (DefaultHeaders)
1523      { DefaultHeaders = def_hdrs; }
1524    }
1525
1526
1527    /**
1528     * Gets the current list of default http headers.
1529     *
1530     * @return an array of header/value pairs.
1531     */
1532    public NVPair[] getDefaultHeaders()
1533    {
1534  //return (NVPair[]) DefaultHeaders.clone();  JDK 1.1 Only
1535
1536  synchronized (DefaultHeaders)
1537  {
1538      NVPair[] headers = new NVPair[DefaultHeaders.length];
1539      System.arraycopy(DefaultHeaders, 0, headers, 0, headers.length);
1540      return headers;
1541  }
1542    }
1543
1544
1545    /**
1546     * Returns the protocol this connection is talking.
1547     *
1548     * @return a string containing the (lowercased) protocol
1549     */
1550    public String getProtocol()
1551    {
1552  switch (Protocol)
1553  {
1554      case HTTP:    return "http";
1555      case HTTPS:   return "https";
1556      case SHTTP:   return "shttp";
1557      case HTTP_NG: return "http-ng";
1558      default:
1559    throw new Error("HTTPClient Internal Error: invalid protocol " +
1560        Protocol);
1561  }
1562    }
1563
1564
1565    /**
1566     * Returns the host this connection is talking to.
1567     *
1568     * @return a string containing the (lowercased) host name.
1569     */
1570    public String getHost()
1571    {
1572  return Host;
1573    }
1574
1575
1576    /**
1577     * Returns the port this connection connects to. This is always the
1578     * actual port number, never -1.
1579     *
1580     * @return the port number
1581     */
1582    public int getPort()
1583    {
1584  return Port;
1585    }
1586
1587
1588    /**
1589     * Returns the host of the proxy this connection is using.
1590     *
1591     * @return a string containing the (lowercased) host name.
1592     */
1593    public String getProxyHost()
1594    {
1595  return Proxy_Host;
1596    }
1597
1598
1599    /**
1600     * Returns the port of the proxy this connection is using.
1601     *
1602     * @return the port number
1603     */
1604    public int getProxyPort()
1605    {
1606  return Proxy_Port;
1607    }
1608
1609
1610    /**
1611     * See if the given uri is compatible with this connection. Compatible
1612     * means that the given uri can be retrieved using this connection
1613     * object.
1614     *
1615     * @param uri  the URI to check
1616     * @return true if they're compatible, false otherwise
1617     * @since V0.3-2
1618     */
1619    public boolean isCompatibleWith(URI uri)
1620    {
1621  if (!uri.getScheme().equals(getProtocol())  ||
1622      !uri.getHost().equalsIgnoreCase(Host))
1623    return false;
1624
1625  int port = uri.getPort();
1626  if (port == -1)
1627      port = URI.defaultPort(uri.getScheme());
1628  return port == Port;
1629    }
1630
1631
1632    /**
1633     * Sets/Resets raw mode. In raw mode all modules are bypassed, meaning
1634     * the automatic handling of authorization requests, redirections,
1635     * cookies, etc. is turned off.
1636     *
1637     * <P>The default is false.
1638     *
1639     * @deprecated This is not really needed anymore; in V0.2 request were
1640     *             synchronous and therefore to do pipelining you needed
1641     *             to disable the processing of responses.
1642     * @see #removeModule(java.lang.Class)
1643     *
1644     * @param raw if true removes all modules (except for the retry module)
1645     */
1646    public void setRawMode(boolean raw)
1647    {
1648  // Don't remove the retry module
1649  String[] modules = { "HTTPClient.CookieModule",
1650           "HTTPClient.RedirectionModule",
1651           "HTTPClient.AuthorizationModule",
1652           "HTTPClient.DefaultModule",
1653           "HTTPClient.TransferEncodingModule",
1654           "HTTPClient.ContentMD5Module",
1655           "HTTPClient.ContentEncodingModule"};
1656
1657  for (int idx=0; idx<modules.length; idx++)
1658  {
1659      try
1660      {
1661    if (raw)
1662        removeModule(Class.forName(modules[idx]));
1663    else
1664        addModule(Class.forName(modules[idx]), -1);
1665      }
1666      catch (ClassNotFoundException cnfe) { }
1667  }
1668    }
1669
1670
1671    /**
1672     * Sets the default timeout value to be used for each new HTTPConnection.
1673     * The default is 0.
1674     *
1675     * @param time the timeout in milliseconds.
1676     * @see #setTimeout(int)
1677     */
1678    public static void setDefaultTimeout(int time)
1679    {
1680  DefaultTimeout = time;
1681    }
1682
1683
1684    /**
1685     * Gets the default timeout value to be used for each new HTTPConnection.
1686     *
1687     * @return the timeout in milliseconds.
1688     * @see #setTimeout(int)
1689     */
1690    public static int getDefaultTimeout()
1691    {
1692  return DefaultTimeout;
1693    }
1694
1695
1696    /**
1697     * Sets the timeout to be used for creating connections and reading
1698     * responses. When a timeout expires the operation will throw an
1699     * InterruptedIOException. The operation may be restarted again
1700     * afterwards. If the operation is not restarted and it is a read
1701     * operation (i.e HTTPResponse.xxxx()) then <code>stop()</code>
1702     * <strong>should</strong> be invoked.
1703     *
1704     * <P>When creating new sockets the timeout will limit the time spent
1705     * doing the host name translation and establishing the connection with
1706     * the server.
1707     *
1708     * <P>The timeout also influences the reading of the response headers.
1709     * However, it does not specify a how long, for example, getStatusCode()
1710     * may take, as might be assumed. Instead it specifies how long a read
1711     * on the socket may take. If the response dribbles in slowly with
1712     * packets arriving quicker than the timeout then the method will
1713     * complete normally. I.e. the exception is only thrown if nothing
1714     * arrives on the socket for the specified time. Furthermore, the
1715     * timeout only influences the reading of the headers, not the reading
1716     * of the body.
1717     *
1718     * <P>Read Timeouts are associated with responses, so that you may change
1719     * this value before each request and it won't affect the reading of
1720     * responses to previous requests.
1721     *
1722     * <P><em>Note:</em> The read timeout only works with JDK 1.1 or later.
1723     * Using this method with JDK 1.0.2 or earlier will only influence the
1724     * connection creation.
1725     *
1726     * @param time the time in milliseconds. A time of 0 means wait
1727     *             indefinitely.
1728     * @see #stop()
1729     */
1730    public void setTimeout(int time)
1731    {
1732  Timeout = time;
1733    }
1734
1735
1736    /**
1737     * Gets the timeout used for reading response data.
1738     *
1739     * @return the current timeout value
1740     * @see #setTimeout(int)
1741     */
1742    public int getTimeout()
1743    {
1744  return Timeout;
1745    }
1746
1747
1748    /**
1749     * Controls whether modules are allowed to prompt the user or pop up
1750     * dialogs if neccessary.
1751     *
1752     * @param allow if true allows modules to interact with user.
1753     */
1754    public void setAllowUserInteraction(boolean allow)
1755    {
1756  AllowUI = allow;
1757    }
1758
1759    /**
1760     * returns whether modules are allowed to prompt or popup dialogs
1761     * if neccessary.
1762     *
1763     * @return true if modules are allowed to interact with user.
1764     */
1765    public boolean getAllowUserInteraction()
1766    {
1767  return AllowUI;
1768    }
1769
1770
1771    /**
1772     * Sets the default allow-user-action.
1773     *
1774     * @param allow if true allows modules to interact with user.
1775     */
1776    public static void setDefaultAllowUserInteraction(boolean allow)
1777    {
1778  DefaultAllowUI = allow;
1779    }
1780
1781    /**
1782     * Gets the default allow-user-action.
1783     *
1784     * @return true if modules are allowed to interact with user.
1785     */
1786    public static boolean getDefaultAllowUserInteraction()
1787    {
1788  return DefaultAllowUI;
1789    }
1790
1791
1792    /**
1793     * Returns the default list of modules.
1794     *
1795     * @return an array of classes
1796     */
1797    public static Class[] getDefaultModules()
1798    {
1799  synchronized(DefaultModuleList)
1800  {
1801      Class[] modules = new Class[DefaultModuleList.size()];
1802      DefaultModuleList.copyInto(modules);
1803      return modules;
1804  }
1805    }
1806
1807    /**
1808     * Adds a module to the default list. It must implement the
1809     * <var>HTTPClientModule</var> interface. If the module is already in
1810     * the list then this method does nothing.
1811     *
1812     * <P>Example:
1813     * <PRE>
1814     * HTTPConnection.addDefaultModule(Class.forName("HTTPClient.CookieModule"), 1);
1815     * </PRE>
1816     * adds the cookie module as the second module in the list.
1817     *
1818     * <P>The default list is created at class initialization time from the
1819     * property <var>HTTPClient.Modules</var>. This must contain a "|"
1820     * separated list of classes in the order they're to be invoked. If this
1821     * property is not set it defaults to:
1822     *
1823     * "HTTPClient.RetryModule | HTTPClient.CookieModule |
1824     *  HTTPClient.RedirectionModule | HTTPClient.AuthorizationModule |
1825     *  HTTPClient.DefaultModule | HTTPClient.TransferEncodingModule |
1826     *  HTTPClient.ContentMD5Module | HTTPClient.ContentEncodingModule"
1827     *
1828     * @see HTTPClientModule
1829     * @param module the module's Class object
1830     * @param pos    the position of this module in the list; if <var>pos</var>
1831     *               >= 0 then this is the absolute position in the list (0 is
1832     *               the first position); if <var>pos</var> < 0 then this is
1833     *               the position relative to the end of the list (-1 means
1834     *               the last element, -2 the second to last element, etc).
1835     * @return       true if module was successfully added; false if the
1836     *               module is already in the list.
1837     * @exception    ArrayIndexOutOfBoundsException if <var>pos</var> >
1838     *               list-size or if <var>pos</var> < -(list-size).
1839     * @exception    ClassCastException if <var>module</var> does not
1840     *               implement the <var>HTTPClientModule</var> interface.
1841     * @exception    RuntimeException if <var>module</var> cannot be
1842     *               instantiated.
1843     */
1844    public static boolean addDefaultModule(Class module, int pos)
1845    {
1846  // check if module implements HTTPClientModule
1847  try
1848      { HTTPClientModule tmp = (HTTPClientModule) module.newInstance(); }
1849  catch (RuntimeException re)
1850      { throw re; }
1851  catch (Exception e)
1852      { throw new RuntimeException(e.toString()); }
1853
1854  synchronized(DefaultModuleList)
1855  {
1856      // check if module already in list
1857      if (DefaultModuleList.contains(module))
1858    return false;
1859
1860      // add module to list
1861      if (pos < 0)
1862    DefaultModuleList.insertElementAt(module,
1863              DefaultModuleList.size()+pos+1);
1864      else
1865    DefaultModuleList.insertElementAt(module, pos);
1866  }
1867
1868  if (DebugConn)
1869      System.err.println("Conn:  Added module " + module.getName() +
1870             " to default list");
1871
1872  return true;
1873    }
1874
1875
1876    /**
1877     * Removes a module from the default list. If the module is not in the
1878     * list it does nothing.
1879     *
1880     * @param module the module's Class object
1881     * @return true if module was successfully removed; false otherwise
1882     */
1883    public static boolean removeDefaultModule(Class module)
1884    {
1885  boolean removed = DefaultModuleList.removeElement(module);
1886
1887  if (DebugConn)
1888      if (removed)
1889    System.err.println("Conn:  Removed module " + module.getName() +
1890           " from default list");
1891
1892  return removed;
1893    }
1894
1895
1896    /**
1897     * Returns the list of modules used currently.
1898     *
1899     * @return an array of classes
1900     */
1901    public Class[] getModules()
1902    {
1903  synchronized(ModuleList)
1904  {
1905      Class[] modules = new Class[ModuleList.size()];
1906      ModuleList.copyInto(modules);
1907      return modules;
1908  }
1909    }
1910
1911
1912    /**
1913     * Adds a module to the current list. It must implement the
1914     * <var>HTTPClientModule</var> interface. If the module is already in
1915     * the list then this method does nothing.
1916     *
1917     * @see HTTPClientModule
1918     * @param module the module's Class object
1919     * @param pos    the position of this module in the list; if <var>pos</var>
1920     *               >= 0 then this is the absolute position in the list (0 is
1921     *               the first position); if <var>pos</var> < 0 then this is
1922     *               the position relative to the end of the list (-1 means
1923     *               the last element, -2 the second to last element, etc).
1924     * @return       true if module was successfully added; false if the
1925     *               module is already in the list.
1926     * @exception    ArrayIndexOutOfBoundsException if <var>pos</var> >
1927     *               list-size or if <var>pos</var> < -(list-size).
1928     * @exception    ClassCastException if <var>module</var> does not
1929     *               implement the <var>HTTPClientModule</var> interface.
1930     * @exception    RuntimeException if <var>module</var> cannot be
1931     *               instantiated.
1932     */
1933    public boolean addModule(Class module, int pos)
1934    {
1935  // check if module implements HTTPClientModule
1936  try
1937      { HTTPClientModule tmp = (HTTPClientModule) module.newInstance(); }
1938  catch (RuntimeException re)
1939      { throw re; }
1940  catch (Exception e)
1941      { throw new RuntimeException(e.toString()); }
1942
1943  synchronized(ModuleList)
1944  {
1945      // check if module already in list
1946      if (ModuleList.contains(module))
1947    return false;
1948
1949      // add module to list
1950      if (pos < 0)
1951    ModuleList.insertElementAt(module, ModuleList.size()+pos+1);
1952      else
1953    ModuleList.insertElementAt(module, pos);
1954  }
1955
1956  return true;
1957    }
1958
1959
1960    /**
1961     * Removes a module from the current list. If the module is not in the
1962     * list it does nothing.
1963     *
1964     * @param module the module's Class object
1965     * @return true if module was successfully removed; false otherwise
1966     */
1967    public boolean removeModule(Class module)
1968    {
1969  if (module == null)  return false;
1970  return ModuleList.removeElement(module);
1971    }
1972
1973
1974    /**
1975     * Sets the current context. The context is used by modules such as
1976     * the AuthorizationModule and the CookieModule which keep lists of
1977     * info that is normally shared between all instances of HTTPConnection.
1978     * This is usually the desired behaviour. However, in some cases one
1979     * would like to simulate multiple independent clients within the
1980     * same application and hence the sharing of such info should be
1981     * restricted. This is where the context comes in. Modules will only
1982     * share their info between requests using the same context (i.e. they
1983     * keep multiple lists, one for each context).
1984     *
1985     * <P>The context may be any object. Contexts are considered equal
1986     * if <code>equals()</code> returns true. Examples of useful context
1987     * objects are threads (e.g. if you are running multiple clients, one
1988     * per thread) and sockets (e.g. if you are implementing a gateway).
1989     *
1990     * <P>When a new HTTPConnection is created it is initialized with a
1991     * default context which is the same for all instances. This method
1992     * must be invoked immediately after a new HTTPConnection is created
1993     * and before any request method is invoked. Furthermore, this method
1994     * may only be called once (i.e. the context is "sticky").
1995     *
1996     * @param context the new context; must be non-null
1997     * @exception IllegalArgumentException if <var>context</var> is null
1998     * @exception RuntimeException if the context has already been set
1999     */
2000    public void setContext(Object context)
2001    {
2002  if (context == null)
2003      throw new IllegalArgumentException("Context must be non-null");
2004  if (Context != null)
2005      throw new RuntimeException("Context already set");
2006
2007  Context = context;
2008    }
2009
2010
2011    /**
2012     * Returns the current context.
2013     *
2014     * @see #setContext(java.lang.Object)
2015     * @return the current context, or the default context if
2016     *         <code>setContext()</code> hasn't been invoked
2017     */
2018    public Object getContext()
2019    {
2020  if (Context != null)
2021      return Context;
2022  else
2023      return dflt_context;
2024    }
2025
2026
2027    /**
2028     * Returns the default context.
2029     *
2030     * @see #setContext(java.lang.Object)
2031     * @return the default context
2032     */
2033    static Object getDefaultContext()
2034    {
2035  return dflt_context;
2036    }
2037
2038
2039    /**
2040     * Adds an authorization entry for the "digest" authorization scheme to
2041     * the list. If an entry already exists for the "digest" scheme and the
2042     * specified realm then it is overwritten.
2043     *
2044     * <P>This is a convenience method and just invokes the corresponding
2045     * method in AuthorizationInfo.
2046     *
2047     * @param realm the realm
2048     * @param user  the username
2049     * @param passw the password
2050     * @see AuthorizationInfo#addDigestAuthorization(java.lang.String, int, java.lang.String, java.lang.String, java.lang.String)
2051     */
2052    public void addDigestAuthorization(String realm, String user, String passwd)
2053    {
2054  AuthorizationInfo.addDigestAuthorization(Host, Port, realm, user,
2055             passwd, getContext());
2056    }
2057
2058
2059    /**
2060     * Adds an authorization entry for the "basic" authorization scheme to
2061     * the list. If an entry already exists for the "basic" scheme and the
2062     * specified realm then it is overwritten.
2063     *
2064     * <P>This is a convenience method and just invokes the corresponding
2065     * method in AuthorizationInfo.
2066     *
2067     * @param realm the realm
2068     * @param user  the username
2069     * @param passw the password
2070     * @see AuthorizationInfo#addBasicAuthorization(java.lang.String, int, java.lang.String, java.lang.String, java.lang.String)
2071     */
2072    public void addBasicAuthorization(String realm, String user, String passwd)
2073    {
2074  AuthorizationInfo.addBasicAuthorization(Host, Port, realm, user,
2075            passwd, getContext());
2076    }
2077
2078
2079    /**
2080     * Sets the default proxy server to use. The proxy will only be used
2081     * for new <var>HTTPConnection</var>s created after this call and will
2082     * not affect currrent instances of <var>HTTPConnection</var>. A null
2083     * or empty string <var>host</var> parameter disables the proxy.
2084     *
2085     * <P>In an application or using the Appletviewer an alternative to
2086     * this method is to set the following properties (either in the
2087     * properties file or on the command line):
2088     * <var>http.proxyHost</var> and <var>http.proxyPort</var>. Whether
2089     * <var>http.proxyHost</var> is set or not determines whether a proxy
2090     * server is used.
2091     *
2092     * <P>If the proxy server requires authorization and you wish to set
2093     * this authorization information in the code, then you may use any
2094     * of the <var>AuthorizationInfo.addXXXAuthorization()</var> methods to
2095     * do so. Specify the same <var>host</var> and <var>port</var> as in
2096     * this method. If you have not given any authorization info and the
2097     * proxy server requires authorization then you will be prompted for
2098     * the necessary info via a popup the first time you do a request.
2099     *
2100     * @see #setCurrentProxy(java.lang.String,int)
2101     * @param  host    the host on which the proxy server resides.
2102     * @param  port    the port the proxy server is listening on.
2103     */
2104    public static void setProxyServer(String host, int port)
2105    {
2106  if (host == null  ||  host.trim().length() == 0)
2107      Default_Proxy_Host = null;
2108  else
2109  {
2110      Default_Proxy_Host = host.trim().toLowerCase();
2111      Default_Proxy_Port = port;
2112  }
2113    }
2114
2115
2116    /**
2117     * Sets the proxy used by this instance. This can be used to override
2118     * the proxy setting inherited from the default proxy setting. A null
2119     * or empty string <var>host</var> parameter disables the proxy.
2120     *
2121     * <P>Note that if you set a proxy for the connection using this
2122     * method, and a request made over this connection is redirected
2123     * to a different server, then the connection used for new server
2124     * will <em>not</em> pick this proxy setting, but instead will use
2125     * the default proxy settings.
2126     *
2127     * @see #setProxyServer(java.lang.String,int)
2128     * @param host the host the proxy runs on
2129     * @param port the port the proxy is listening on
2130     */
2131    public synchronized void setCurrentProxy(String host, int port)
2132    {
2133  if (host == null  ||  host.trim().length() == 0)
2134      Proxy_Host = null;
2135  else
2136  {
2137      Proxy_Host = host.trim().toLowerCase();
2138      if (port <= 0)
2139    Proxy_Port = 80;
2140      else
2141    Proxy_Port = port;
2142  }
2143
2144  // the proxy might be talking a different version, so renegotiate
2145  switch(Protocol)
2146  {
2147      case HTTP:
2148      case HTTPS:
2149    if (force_1_0)
2150    {
2151        ServerProtocolVersion  = HTTP_1_0;
2152        ServProtVersKnown      = true;
2153        RequestProtocolVersion = "HTTP/1.0";
2154    }
2155    else
2156    {
2157        ServerProtocolVersion  = HTTP_1_1;
2158        ServProtVersKnown      = false;
2159        RequestProtocolVersion = "HTTP/1.1";
2160    }
2161    break;
2162      case HTTP_NG:
2163    ServerProtocolVersion  = -1;    /* Unknown */
2164    ServProtVersKnown      = false;
2165    RequestProtocolVersion = "";
2166    break;
2167      case SHTTP:
2168    ServerProtocolVersion  = -1;    /* Unknown */
2169    ServProtVersKnown      = false;
2170    RequestProtocolVersion = "Secure-HTTP/1.3";
2171    break;
2172      default:
2173    throw new Error("HTTPClient Internal Error: invalid protocol " +
2174        Protocol);
2175  }
2176
2177  KeepAliveUnknown = true;
2178  DoesKeepAlive    = false;
2179
2180  input_demux = null;
2181  early_stall = null;
2182  late_stall  = null;
2183  prev_resp   = null;
2184    }
2185
2186
2187    /**
2188     * Add <var>host</var> to the list of hosts which should be accessed
2189     * directly, not via any proxy set by <code>setProxyServer()</code>.
2190     *
2191     * <P>The <var>host</var> may be any of:
2192     * <UL>
2193     * <LI>a complete host name (e.g. "www.disney.com")
2194     * <LI>a domain name; domain names must begin with a dot (e.g.
2195     *     ".disney.com")
2196     * <LI>an IP-address (e.g. "12.34.56.78")
2197     * <LI>an IP-subnet, specified as an IP-address and a netmask separated
2198     *     by a "/" (e.g. "34.56.78/255.255.255.192"); a 0 bit in the netmask
2199     *     means that that bit won't be used in the comparison (i.e. the
2200     *     addresses are AND'ed with the netmask before comparison).
2201     * </UL>
2202     *
2203     * <P>The two properties <var>HTTPClient.nonProxyHosts</var> and
2204     * <var>http.nonProxyHosts</var> are used when this class is loaded to
2205     * initialize the list of non-proxy hosts. The second property is only
2206     * read if the first one is not set; the second property is also used
2207     * the JDK's URLConnection. These properties must contain a "|"
2208     * separated list of entries which conform to the above rules for the
2209     * <var>host</var> parameter (e.g. "11.22.33.44|.disney.com").
2210     *
2211     * @param host a host name, domain name, IP-address or IP-subnet.
2212     * @exception ParseException if the length of the netmask does not match
2213     *                           the length of the IP-address
2214     */
2215    public static void dontProxyFor(String host)  throws ParseException
2216    {
2217  host = host.trim().toLowerCase();
2218
2219  // check for domain name
2220
2221  if (host.charAt(0) == '.')
2222  {
2223      if (!non_proxy_dom_list.contains(host))
2224    non_proxy_dom_list.addElement(host);
2225      return;
2226  }
2227
2228
2229  // check for host name
2230
2231  for (int idx=0; idx<host.length(); idx++)
2232  {
2233      if (!Character.isDigit(host.charAt(idx))  &&
2234    host.charAt(idx) != '.'  &&  host.charAt(idx) != '/')
2235      {
2236    non_proxy_host_list.put(host, "");
2237    return;
2238      }
2239  }
2240
2241
2242  // must be an IP-address
2243
2244  byte[] ip_addr;
2245  byte[] ip_mask;
2246  int slash;
2247  if ((slash = host.indexOf('/')) != -1)  // IP subnet
2248  {
2249      ip_addr = string2arr(host.substring(0, slash));
2250      ip_mask = string2arr(host.substring(slash+1));
2251      if (ip_addr.length != ip_mask.length)
2252    throw new ParseException("length of IP-address (" +
2253        ip_addr.length + ") != length of netmask (" +
2254        ip_mask.length + ")");
2255  }
2256  else
2257  {
2258      ip_addr = string2arr(host);
2259      ip_mask = new byte[ip_addr.length];
2260      for (int idx=0; idx<ip_mask.length; idx++)
2261    ip_mask[idx] = (byte) 255;
2262  }
2263
2264
2265  // check if addr or subnet already exists
2266
2267  ip_loop: for (int idx=0; idx<non_proxy_addr_list.size(); idx++)
2268  {
2269      byte[] addr = (byte[]) non_proxy_addr_list.elementAt(idx);
2270      byte[] mask = (byte[]) non_proxy_mask_list.elementAt(idx);
2271      if (addr.length != ip_addr.length)  continue;
2272
2273      for (int idx2=0; idx2<addr.length; idx2++)
2274      {
2275    if ((ip_addr[idx2] & mask[idx2]) != (addr[idx2] & mask[idx2]) ||
2276        (mask[idx2] != ip_mask[idx2]))
2277        continue ip_loop;
2278      }
2279
2280      return;      // already exists
2281  }
2282  non_proxy_addr_list.addElement(ip_addr);
2283  non_proxy_mask_list.addElement(ip_mask);
2284    }
2285
2286
2287    /**
2288     * Convenience method to add a number of hosts at once. If any one
2289     * host is null or cannot be parsed it is ignored.
2290     *
2291     * @param hosts The list of hosts to set
2292     * @see #dontProxyFor(java.lang.String)
2293     * @since V0.3-2
2294     */
2295    public static void dontProxyFor(String[] hosts)
2296    {
2297        if (hosts == null  ||  hosts.length == 0)
2298      return;
2299
2300        for (int idx=0; idx<hosts.length; idx++)
2301        {
2302            try
2303            {
2304                if (hosts[idx] != null)
2305                    dontProxyFor(hosts[idx]);
2306            }
2307            catch(ParseException pe)
2308            {
2309    // ignore it
2310            }
2311        }
2312    }
2313
2314
2315    /**
2316     * Remove <var>host</var> from the list of hosts for which the proxy
2317     * should not be used. The syntax for <var>host</var> is specified in
2318     * <code>dontProxyFor()</code>.
2319     *
2320     * @param host a host name, domain name, IP-address or IP-subnet.
2321     * @return true if the remove was sucessful, false otherwise
2322     * @exception ParseException if the length of the netmask does not match
2323     *                           the length of the IP-address
2324     * @see #dontProxyFor(java.lang.String)
2325     */
2326    public static boolean doProxyFor(String host)  throws ParseException
2327    {
2328  host = host.trim().toLowerCase();
2329
2330  // check for domain name
2331
2332  if (host.charAt(0) == '.')
2333      return non_proxy_dom_list.removeElement(host);
2334
2335
2336  // check for host name
2337
2338  for (int idx=0; idx<host.length(); idx++)
2339  {
2340      if (!Character.isDigit(host.charAt(idx))  &&
2341    host.charAt(idx) != '.'  &&  host.charAt(idx) != '/')
2342    return (non_proxy_host_list.remove(host) != null);
2343  }
2344
2345
2346  // must be an IP-address
2347
2348  byte[] ip_addr;
2349  byte[] ip_mask;
2350  int slash;
2351  if ((slash = host.indexOf('/')) != -1)  // IP subnet
2352  {
2353      ip_addr = string2arr(host.substring(0, slash));
2354      ip_mask = string2arr(host.substring(slash+1));
2355      if (ip_addr.length != ip_mask.length)
2356    throw new ParseException("length of IP-address (" +
2357        ip_addr.length + ") != length of netmask (" +
2358        ip_mask.length + ")");
2359  }
2360  else
2361  {
2362      ip_addr = string2arr(host);
2363      ip_mask = new byte[ip_addr.length];
2364      for (int idx=0; idx<ip_mask.length; idx++)
2365    ip_mask[idx] = (byte) 255;
2366  }
2367
2368  ip_loop: for (int idx=0; idx<non_proxy_addr_list.size(); idx++)
2369  {
2370      byte[] addr = (byte[]) non_proxy_addr_list.elementAt(idx);
2371      byte[] mask = (byte[]) non_proxy_mask_list.elementAt(idx);
2372      if (addr.length != ip_addr.length)  continue;
2373
2374      for (int idx2=0; idx2<addr.length; idx2++)
2375      {
2376    if ((ip_addr[idx2] & mask[idx2]) != (addr[idx2] & mask[idx2]) ||
2377        (mask[idx2] != ip_mask[idx2]))
2378        continue ip_loop;
2379      }
2380
2381      non_proxy_addr_list.removeElementAt(idx);
2382      non_proxy_mask_list.removeElementAt(idx);
2383      return true;
2384  }
2385  return false;
2386    }
2387
2388
2389    /**
2390     * Turn an IP-address string into an array (e.g. "12.34.56.78" into
2391     * { 12, 34, 56, 78 }).
2392     *
2393     * @param ip IP-address
2394     * @return IP-address in network byte order
2395     */
2396    private static byte[] string2arr(String ip)
2397    {
2398  byte[] arr;
2399  char[] ip_char = new char[ip.length()];
2400  ip.getChars(0, ip_char.length, ip_char, 0);
2401
2402  int cnt = 0;
2403  for (int idx=0; idx<ip_char.length; idx++)
2404      if (ip_char[idx] == '.') cnt++;
2405  arr = new byte[cnt+1];
2406
2407  cnt = 0;
2408  int pos = 0;
2409  for (int idx=0; idx<ip_char.length; idx++)
2410      if (ip_char[idx] == '.')
2411      {
2412    arr[cnt] = (byte) Integer.parseInt(ip.substring(pos, idx));
2413    cnt++;
2414    pos = idx+1;
2415      }
2416  arr[cnt] = (byte) Integer.parseInt(ip.substring(pos));
2417
2418  return arr;
2419    }
2420
2421
2422    /**
2423     * Sets the SOCKS server to use. The server will only be used
2424     * for new HTTPConnections created after this call and will not affect
2425     * currrent instances of HTTPConnection. A null or empty string host
2426     * parameter disables SOCKS.
2427     * <P>The code will try to determine the SOCKS version to use at
2428     * connection time. This might fail for a number of reasons, however,
2429     * in which case you must specify the version explicitly.
2430     *
2431     * @see #setSocksServer(java.lang.String, int, int)
2432     * @param  host    the host on which the proxy server resides. The port
2433     *                 used is the default port 1080.
2434     */
2435    public static void setSocksServer(String host)
2436    {
2437  setSocksServer(host, 1080);
2438    }
2439
2440
2441    /**
2442     * Sets the SOCKS server to use. The server will only be used
2443     * for new HTTPConnections created after this call and will not affect
2444     * currrent instances of HTTPConnection. A null or empty string host
2445     * parameter disables SOCKS.
2446     * <P>The code will try to determine the SOCKS version to use at
2447     * connection time. This might fail for a number of reasons, however,
2448     * in which case you must specify the version explicitly.
2449     *
2450     * @see #setSocksServer(java.lang.String, int, int)
2451     * @param  host    the host on which the proxy server resides.
2452     * @param  port    the port the proxy server is listening on.
2453     */
2454    public static void setSocksServer(String host, int port)
2455    {
2456  if (port <= 0)
2457      port = 1080;
2458
2459  if (host == null  ||  host.length() == 0)
2460      Default_Socks_client = null;
2461  else
2462      Default_Socks_client = new SocksClient(host, port);
2463    }
2464
2465
2466    /**
2467     * Sets the SOCKS server to use. The server will only be used
2468     * for new HTTPConnections created after this call and will not affect
2469     * currrent instances of HTTPConnection. A null or empty string host
2470     * parameter disables SOCKS.
2471     *
2472     * <P>In an application or using the Appletviewer an alternative to
2473     * this method is to set the following properties (either in the
2474     * properties file or on the command line):
2475     * <var>HTTPClient.socksHost</var>, <var>HTTPClient.socksPort</var>
2476     * and <var>HTTPClient.socksVersion</var>. Whether
2477     * <var>HTTPClient.socksHost</var> is set or not determines whether a
2478     * SOCKS server is used; if <var>HTTPClient.socksPort</var> is not set
2479     * it defaults to 1080; if <var>HTTPClient.socksVersion</var> is not
2480     * set an attempt will be made to automatically determine the version
2481     * used by the server.
2482     *
2483     * <P>Note: If you have also set a proxy server then a connection
2484     * will be made to the SOCKS server, which in turn then makes a
2485     * connection to the proxy server (possibly via other SOCKS servers),
2486     * which in turn makes the final connection.
2487     *
2488     * <P>If the proxy server is running SOCKS version 5 and requires
2489     * username/password authorization, and you wish to set
2490     * this authorization information in the code, then you may use the
2491     * <var>AuthorizationInfo.addAuthorization()</var> method to do so.
2492     * Specify the same <var>host</var> and <var>port</var> as in this
2493     * method, give the <var>scheme</var> "SOCKS5" and the <var>realm</var>
2494     * "USER/PASS", set the <var>cookie</var> to null and the
2495     * <var>params</var> to an array containing a single <var>NVPair</var>
2496     * in turn containing the username and password. Example:
2497     * <PRE>
2498     *     NVPair[] up = { new NVPair(username, password) };
2499     *     AuthorizationInfo.addAuthorization(host, port, "SOCKS5", "USER/PASS",
2500     *                                        null, up);
2501     * </PRE>
2502     * If you have not given any authorization info and the proxy server
2503     * requires authorization then you will be prompted for the necessary
2504     * info via a popup the first time you do a request.
2505     *
2506     * @param  host    the host on which the proxy server resides.
2507     * @param  port    the port the proxy server is listening on.
2508     * @param  version the SOCKS version the server is running. Currently
2509     *                 this must be '4' or '5'.
2510     * @exception SocksException If <var>version</var> is not '4' or '5'.
2511     */
2512    public static void setSocksServer(String host, int port, int version)
2513      throws SocksException
2514    {
2515  if (port <= 0)
2516      port = 1080;
2517
2518  if (host == null  ||  host.length() == 0)
2519      Default_Socks_client = null;
2520  else
2521      Default_Socks_client = new SocksClient(host, port, version);
2522    }
2523
2524
2525    /**
2526     * Removes the #... part. Returns the stripped name, or "" if either
2527     * the <var>file</var> is null or is the empty string (after stripping).
2528     *
2529     * @param file the name to strip
2530     * @return the stripped name
2531     */
2532    private final String stripRef(String file)
2533    {
2534  if (file == null)  return "";
2535
2536  int hash = file.indexOf('#');
2537  if (hash != -1)
2538      file = file.substring(0,hash);
2539
2540  return file.trim();
2541    }
2542
2543
2544    // private helper methods
2545
2546    /**
2547     * Sets up the request, creating the list of headers to send and
2548     * creating instances of the modules.
2549     *
2550     * @param  method   GET, POST, etc.
2551     * @param  resource the resource
2552     * @param  headers  an array of headers to be used
2553     * @param  entity   the entity (or null)
2554     * @return the response.
2555     * @exception java.io.IOException when an exception is returned from
2556     *                                the socket.
2557     * @exception ModuleException if an exception is encountered in any module.
2558     */
2559    private HTTPResponse setupRequest(String method, String resource,
2560              NVPair[] headers, byte[] entity,
2561              HttpOutputStream stream)
2562    throws IOException, ModuleException
2563    {
2564  Request req = new Request(this, method, resource,
2565          mergedHeaders(headers), entity, stream,
2566          AllowUI);
2567  RequestList.addToEnd(req);
2568
2569  try
2570  {
2571      HTTPResponse resp = new HTTPResponse(gen_mod_insts(), Timeout, req);
2572      handleRequest(req, resp, null, true);
2573      return resp;
2574  }
2575  finally
2576      { RequestList.remove(req); }
2577    }
2578
2579
2580    /**
2581     * This merges built-in default headers, user-specified default headers,
2582     * and method-specified headers. Method-specified take precedence over
2583     * user defaults, which take precedence over built-in defaults.
2584     *
2585     * The following headers are removed if found: "Host" and
2586     * "Content-length".
2587     *
2588     * @param  spec   the headers specified in the call to the method
2589     * @return an array consisting of merged headers.
2590     */
2591    private NVPair[] mergedHeaders(NVPair[] spec)
2592    {
2593  int spec_len = (spec != null ? spec.length : 0),
2594      defs_len;
2595  NVPair[] merged;
2596
2597  synchronized (DefaultHeaders)
2598  {
2599      defs_len = (DefaultHeaders != null ? DefaultHeaders.length : 0);
2600      merged   = new NVPair[spec_len + defs_len];
2601
2602      // copy default headers
2603      System.arraycopy(DefaultHeaders, 0, merged, 0, defs_len);
2604  }
2605
2606  // merge in selected headers
2607  int sidx, didx = defs_len;
2608  for (sidx=0; sidx<spec_len; sidx++)
2609  {
2610      String s_name = spec[sidx].getName().trim();
2611      if (s_name.equalsIgnoreCase("Content-length")  ||
2612    s_name.equalsIgnoreCase("Host"))
2613    continue;
2614
2615      int search;
2616      for (search=0; search<didx; search++)
2617      {
2618    if (merged[search].getName().trim().equalsIgnoreCase(s_name))
2619        break;
2620      }
2621
2622      merged[search] = spec[sidx];
2623      if (search == didx) didx++;
2624  }
2625
2626  if (didx < merged.length)
2627      merged = Util.resizeArray(merged, didx);
2628
2629  return merged;
2630    }
2631
2632
2633    /**
2634     * Generate an array of instances of the current modules.
2635     */
2636    private HTTPClientModule[] gen_mod_insts()
2637    {
2638  synchronized (ModuleList)
2639  {
2640      HTTPClientModule[] mod_insts =
2641    new HTTPClientModule[ModuleList.size()];
2642
2643      for (int idx=0; idx<ModuleList.size(); idx++)
2644      {
2645    Class mod = (Class) ModuleList.elementAt(idx);
2646    try
2647        { mod_insts[idx] = (HTTPClientModule) mod.newInstance(); }
2648    catch (Exception e)
2649    {
2650        throw new Error("HTTPClient Internal Error: could not " +
2651            "create instance of " + mod.getName() +
2652            " -\n" + e);
2653    }
2654      }
2655
2656      return mod_insts;
2657  }
2658    }
2659
2660
2661    /**
2662     * handles the Request. First the request handler for each module is
2663     * is invoked, and then if no response was generated the request is
2664     * sent.
2665     *
2666     * @param  req        the Request
2667     * @param  http_resp  the HTTPResponse
2668     * @param  resp       the Response
2669     * @param  usemodules if false then skip module loop
2670     * @exception IOException if any module or sendRequest throws it
2671     * @exception ModuleException if any module throws it
2672     */
2673    void handleRequest(Request req, HTTPResponse http_resp, Response resp,
2674           boolean usemodules)
2675    throws IOException, ModuleException
2676    {
2677  Response[]         rsp_arr = { resp };
2678  HTTPClientModule[] modules = http_resp.getModules();
2679
2680
2681  // invoke requestHandler for each module
2682
2683  if (usemodules)
2684  doModules: for (int idx=0; idx<modules.length; idx++)
2685  {
2686      int sts = modules[idx].requestHandler(req, rsp_arr);
2687      switch (sts)
2688      {
2689    case REQ_CONTINUE:  // continue processing
2690        break;
2691
2692    case REQ_RESTART:  // restart processing with first module
2693        idx = -1;
2694        continue doModules;
2695
2696    case REQ_SHORTCIRC:  // stop processing and send
2697        break doModules;
2698
2699    case REQ_RESPONSE:  // go to phase 2
2700    case REQ_RETURN:    // return response immediately
2701        if (rsp_arr[0] == null)
2702      throw new Error("HTTPClient Internal Error: no " +
2703          "response returned by module " +
2704          modules[idx].getClass().getName());
2705        http_resp.set(req, rsp_arr[0]);
2706        if (req.getStream() != null)
2707      req.getStream().ignoreData(req);
2708        if (req.internal_subrequest)  return;
2709        if (sts == REQ_RESPONSE)
2710      http_resp.handleResponse();
2711        else
2712      http_resp.init(rsp_arr[0]);
2713        return;
2714
2715    case REQ_NEWCON_RST:  // new connection
2716        if (req.internal_subrequest)  return;
2717        req.getConnection().
2718          handleRequest(req, http_resp, rsp_arr[0], true);
2719        return;
2720
2721    case REQ_NEWCON_SND:  // new connection, send immediately
2722        if (req.internal_subrequest)  return;
2723        req.getConnection().
2724          handleRequest(req, http_resp, rsp_arr[0], false);
2725        return;
2726
2727    default:    // not valid
2728        throw new Error("HTTPClient Internal Error: invalid status"+
2729            " " + sts + " returned by module " +
2730            modules[idx].getClass().getName());
2731      }
2732  }
2733
2734  if (req.internal_subrequest)  return;
2735
2736
2737  // Send the request across the wire
2738
2739  if (req.getStream() != null  &&  req.getStream().getLength() == -1)
2740  {
2741      if (!ServProtVersKnown  ||  ServerProtocolVersion < HTTP_1_1  ||
2742    no_chunked)
2743      {
2744    req.getStream().goAhead(req, null, http_resp.getTimeout());
2745    http_resp.set(req, req.getStream());
2746      }
2747      else
2748      {
2749    // add Transfer-Encoding header if necessary
2750    int idx;
2751    NVPair[] hdrs = req.getHeaders();
2752    for (idx=0; idx<hdrs.length; idx++)
2753        if (hdrs[idx].getName().equalsIgnoreCase("Transfer-Encoding"))
2754      break;
2755
2756    if (idx == hdrs.length)
2757    {
2758        hdrs = Util.resizeArray(hdrs, idx+1);
2759        hdrs[idx] = new NVPair("Transfer-Encoding", "chunked");
2760        req.setHeaders(hdrs);
2761    }
2762    else
2763    {
2764        String v = hdrs[idx].getValue();
2765        try
2766        {
2767      if (!Util.hasToken(v, "chunked"))
2768          hdrs[idx] = new NVPair("Transfer-Encoding",
2769               v + ", chunked");
2770        }
2771        catch (ParseException pe)
2772      { throw new IOException(pe.toString()); }
2773    }
2774
2775    http_resp.set(req, sendRequest(req, http_resp.getTimeout()));
2776      }
2777  }
2778  else
2779      http_resp.set(req, sendRequest(req, http_resp.getTimeout()));
2780
2781  if (req.aborted)  throw new IOException("Request aborted by user");
2782    }
2783
2784
2785    /** These mark the response to stall the next request on, if any */
2786    private Response early_stall = null;
2787    private Response late_stall  = null;
2788    private Response prev_resp   = null;
2789    /** This marks the socket output stream as still being used */
2790    private boolean  output_finished = true;
2791
2792    /**
2793     * sends the request over the line.
2794     *
2795     * @param  req         the request
2796     * @param  con_timeout the timeout to use when establishing a socket
2797     *                     connection; an InterruptedIOException is thrown
2798     *                     if the procedure times out.
2799     * @return the response.
2800     * @exception IOException     if thrown by the socket
2801     * @exception InterruptedIOException if the connection is not established
2802     *                                   within the specified timeout
2803     * @exception ModuleException if any module throws it during the SSL-
2804     *                            tunneling handshake
2805     */
2806    Response sendRequest(Request req, int con_timeout)
2807    throws IOException, ModuleException
2808    {
2809  ByteArrayOutputStream hdr_buf = new ByteArrayOutputStream(600);
2810  Response              resp = null;
2811  boolean          keep_alive;
2812
2813
2814  // The very first request is special in that we need its response
2815  // before any further requests may be made. This is to set things
2816  // like the server version.
2817
2818  if (early_stall != null)
2819  {
2820      try
2821      {
2822    if (DebugConn)
2823        System.err.println("Conn:  Early-stalling Request: " +
2824               req.getMethod() + " " +
2825               req.getRequestURI());
2826
2827    synchronized(early_stall)
2828    {
2829        // wait till the response is received
2830        try
2831      { early_stall.getVersion(); }
2832        catch (IOException ioe)
2833      { }
2834        early_stall = null;
2835    }
2836      }
2837      catch (NullPointerException npe)
2838    { }
2839  }
2840
2841
2842  String[] con_hdrs = assembleHeaders(req, hdr_buf);
2843
2844
2845  // determine if the connection should be kept alive after this
2846  // request
2847
2848  try
2849  {
2850      if (ServerProtocolVersion >= HTTP_1_1  &&
2851     !Util.hasToken(con_hdrs[0], "close")
2852    ||
2853    ServerProtocolVersion == HTTP_1_0  &&
2854     Util.hasToken(con_hdrs[0], "keep-alive")
2855    )
2856    keep_alive = true;
2857      else
2858    keep_alive = false;
2859  }
2860  catch (ParseException pe)
2861      { throw new IOException(pe.toString()); }
2862
2863
2864  synchronized(this)
2865  {
2866  // Sometimes we must stall the pipeline until the previous request
2867  // has been answered. However, if we are going to open up a new
2868  // connection anyway we don't really need to stall.
2869
2870  if (late_stall != null)
2871  {
2872      if (input_demux != null  ||  KeepAliveUnknown)
2873      {
2874    if (DebugConn)
2875        System.err.println("Conn:  Stalling Request: " +
2876           req.getMethod() + " " + req.getRequestURI());
2877
2878    try      // wait till the response is received
2879    {
2880        late_stall.getVersion();
2881        if (KeepAliveUnknown)
2882      determineKeepAlive(late_stall);
2883    }
2884    catch (IOException ioe)
2885        { }
2886      }
2887
2888      late_stall = null;
2889  }
2890
2891
2892  /* POSTs must not be pipelined because of problems if the connection
2893   * is aborted. Since it is generally impossible to know what urls
2894   * POST will influence it is impossible to determine if a sequence
2895   * of requests containing a POST is idempotent.
2896   * Also, for retried requests we don't want to pipeline either.
2897   */
2898  if ((req.getMethod().equals("POST")  ||  req.dont_pipeline)  &&
2899      prev_resp != null  &&  input_demux != null)
2900  {
2901      if (DebugConn)
2902    System.err.println("Conn:  Stalling Request: " +
2903           req.getMethod() + " " + req.getRequestURI());
2904
2905      try        // wait till the response is received
2906    { prev_resp.getVersion(); }
2907      catch (IOException ioe)
2908    { }
2909  }
2910
2911
2912  // If the previous request used an output stream, then wait till
2913  // all the data has been written
2914
2915  if (!output_finished)
2916  {
2917      try
2918    { wait(); }
2919      catch (InterruptedException ie)
2920    { throw new IOException(ie.toString()); }
2921  }
2922
2923
2924  if (req.aborted)  throw new IOException("Request aborted by user");
2925
2926  int try_count = 3;
2927  /* what a hack! This is to handle the case where the server closes
2928   * the connection but we don't realize it until we try to send
2929   * something. The problem is that we only get IOException, but
2930   * we need a finer specification (i.e. whether it's an EPIPE or
2931   * something else); I don't trust relying on the message part
2932   * of IOException (which on SunOS/Solaris gives 'Broken pipe',
2933   * but what on Windoze/Mac?).
2934   */
2935
2936  while (try_count-- > 0)
2937  {
2938      try
2939      {
2940    // get a client socket
2941
2942    Socket sock;
2943    if (input_demux == null  ||
2944        (sock = input_demux.getSocket()) == null)
2945    {
2946        sock = getSocket(con_timeout);
2947
2948        if (Protocol == HTTPS)
2949        {
2950      if (Proxy_Host != null)
2951      {
2952          Socket[] sarr = { sock };
2953          resp = enableSSLTunneling(sarr, req, con_timeout);
2954          if (resp != null)
2955          {
2956        resp.final_resp = true;
2957        return resp;
2958          }
2959          sock = sarr[0];
2960      }
2961
2962      sock = (sslContext.getSocketFactory()).createSocket(sock, Host, Port, true);
2963
2964      checkCert(((SSLSocket) sock).getSession().
2965           getPeerCertificateChain()[0], Host);
2966        }
2967
2968        input_demux = new StreamDemultiplexor(Protocol, sock, this);
2969        DemuxList.addToEnd(input_demux);
2970        KeepAliveReqLeft = KeepAliveReqMax;
2971    }
2972
2973    if (req.aborted)
2974        throw new IOException("Request aborted by user");
2975
2976    if (DebugConn)
2977    {
2978        System.err.println("Conn:  Sending Request: ");
2979        System.err.println();
2980        hdr_buf.writeTo(System.err);
2981    }
2982
2983
2984    // Send headers
2985
2986    OutputStream sock_out = sock.getOutputStream();
2987    if (haveMSLargeWritesBug)
2988        sock_out = new MSLargeWritesBugStream(sock_out);
2989
2990    hdr_buf.writeTo(sock_out);
2991
2992
2993    // Wait for "100 Continue" status if necessary
2994
2995    try
2996    {
2997        if (ServProtVersKnown  &&
2998      ServerProtocolVersion >= HTTP_1_1  &&
2999      Util.hasToken(con_hdrs[1], "100-continue"))
3000        {
3001      resp = new Response(req, (Proxy_Host != null && Protocol != HTTPS), input_demux);
3002      resp.timeout = 60;
3003      if (resp.getContinue() != 100)
3004          break;
3005        }
3006    }
3007    catch (ParseException pe)
3008        { throw new IOException(pe.toString()); }
3009    catch (InterruptedIOException iioe)
3010        { }
3011    finally
3012        { if (resp != null)  resp.timeout = 0; }
3013
3014
3015    // POST/PUT data
3016
3017    if (req.getData() != null  &&  req.getData().length > 0)
3018    {
3019        if (req.delay_entity > 0)
3020        {
3021                        // wait for something on the network; check available()
3022                        // roughly every 100 ms
3023
3024      long num_units = req.delay_entity / 100;
3025      long one_unit  = req.delay_entity / num_units;
3026
3027                        for (int idx=0; idx<num_units; idx++)
3028                        {
3029                            if (input_demux.available(null) != 0)
3030                                break;
3031                            try { Thread.sleep(one_unit); }
3032                            catch (InterruptedException ie) { }
3033                        }
3034
3035                        if (input_demux.available(null) == 0)
3036          sock_out.write(req.getData()); // he's still waiting
3037      else
3038          keep_alive = false;    // Uh oh!
3039        }
3040        else
3041      sock_out.write(req.getData());
3042    }
3043
3044    if (req.getStream() != null)
3045        req.getStream().goAhead(req, sock_out, 0);
3046    else
3047        sock_out.flush();
3048
3049
3050    // get a new response.
3051    // Note: this does not do a read on the socket.
3052
3053    if (resp == null)
3054        resp = new Response(req, (Proxy_Host != null &&
3055               Protocol != HTTPS),
3056          input_demux);
3057      }
3058      catch (IOException ioe)
3059      {
3060    if (DebugConn)
3061    {
3062        System.err.print("Conn:  ");
3063        ioe.printStackTrace();
3064    }
3065
3066    closeDemux(ioe, true);
3067
3068    if (try_count == 0  ||  ioe instanceof UnknownHostException  ||
3069        ioe instanceof InterruptedIOException  ||  req.aborted)
3070        throw ioe;
3071
3072    if (DebugConn)
3073        System.err.println("Conn:  Retrying request");
3074    continue;
3075      }
3076
3077      break;
3078  }
3079
3080  prev_resp = resp;
3081
3082
3083  // close the stream after this response if necessary
3084
3085  if ((!KeepAliveUnknown && !DoesKeepAlive)  ||  !keep_alive  ||
3086      (KeepAliveReqMax != -1  &&  KeepAliveReqLeft-- == 0))
3087  {
3088      input_demux.markForClose(resp);
3089      input_demux = null;
3090  }
3091  else
3092      input_demux.restartTimer();
3093
3094  if (DebugConn)
3095  {
3096      if (KeepAliveReqMax != -1)
3097    System.err.println("Conn:  Number of requests left: "+
3098            KeepAliveReqLeft);
3099  }
3100
3101
3102  /* We don't pipeline the first request, as we need some info
3103   * about the server (such as which http version it complies with)
3104   */
3105  if (!ServProtVersKnown)
3106      { early_stall = resp; resp.markAsFirstResponse(req); }
3107
3108  /* Also don't pipeline until we know if the server supports
3109   * keep-alive's or not.
3110   * Note: strictly speaking, HTTP/1.0 keep-alives don't mean we can
3111   *       pipeline requests. I seem to remember some (beta?) version
3112   *       of Netscape's Enterprise server which barfed if you tried
3113   *       push requests down it's throat w/o waiting for the previous
3114   *       response first. However, I've not been able to find such a
3115   *       server lately, and so I'm taking the risk and assuming we
3116   *       can in fact pipeline requests to HTTP/1.0 servers.
3117   */
3118  if (KeepAliveUnknown  ||
3119    // We don't pipeline POST's ...
3120      !IdempotentSequence.methodIsIdempotent(req.getMethod())  ||
3121      req.dont_pipeline  ||  // Retries disable pipelining too
3122      NeverPipeline)  // Emergency measure: prevent all pipelining
3123      { late_stall = resp; }
3124
3125
3126  /* If there is an output stream then just tell the other threads to
3127   * wait; the stream will notify() when it's done. If there isn't any
3128   * stream then wake up a waiting thread (if any).
3129   */
3130  if (req.getStream() != null)
3131      output_finished = false;
3132  else
3133  {
3134      output_finished = true;
3135      notify();
3136  }
3137
3138
3139  // Looks like were finally done
3140
3141  if (DebugConn) System.err.println("Conn:  Request sent");
3142  }
3143
3144  return resp;
3145    }
3146
3147    /**
3148     * Gets a socket. Creates a socket to the proxy if set, or else to the
3149     * actual destination.
3150     *
3151     * @param con_timeout if not 0 then start a new thread to establish the
3152     *                    the connection and join(con_timeout) it. If the
3153     *                    join() times out an InteruptedIOException is thrown.
3154     */
3155    private Socket getSocket(int con_timeout)  throws IOException
3156    {
3157  Socket sock = null;
3158
3159  String actual_host;
3160  int    actual_port;
3161
3162  if (Proxy_Host != null)
3163  {
3164      actual_host = Proxy_Host;
3165      actual_port = Proxy_Port;
3166  }
3167  else
3168  {
3169      actual_host = Host;
3170      actual_port = Port;
3171  }
3172
3173  if (DebugConn)
3174      System.err.println("Conn:  Creating Socket: " + actual_host + ":" +
3175        actual_port);
3176
3177  if (con_timeout == 0)    // normal connection establishment
3178  {
3179      if (Socks_client != null)
3180    sock = Socks_client.getSocket(actual_host, actual_port);
3181      else
3182      {
3183    // try all A records
3184    InetAddress[] addr_list = InetAddress.getAllByName(actual_host);
3185    for (int idx=0; idx<addr_list.length; idx++)
3186    {
3187        try
3188        {
3189      sock = new Socket(addr_list[idx], actual_port);
3190      break;    // success
3191        }
3192        catch (SocketException se)  // should be NoRouteToHostException
3193        {
3194      if (idx == addr_list.length-1)
3195          throw se;  // we tried them all
3196        }
3197    }
3198      }
3199  }
3200  else
3201  {
3202      EstablishConnection con =
3203    new EstablishConnection(actual_host, actual_port, Socks_client);
3204      con.start();
3205      try
3206    { con.join((long) con_timeout); }
3207      catch (InterruptedException ie)
3208    { }
3209
3210      if (con.getException() != null)
3211    throw con.getException();
3212      if ((sock = con.getSocket()) == null)
3213      {
3214    con.forget();
3215    if ((sock = con.getSocket()) == null)
3216        throw new InterruptedIOException("Connection establishment timed out");
3217      }
3218  }
3219
3220  return sock;
3221    }
3222
3223
3224    /**
3225     * Enable SSL Tunneling if we're talking to a proxy. See ietf draft
3226     * draft-luotonen-ssl-tunneling-03 for more info.
3227     *
3228     * @param sock    the socket
3229     * @param req     the request initiating this connection
3230     * @param timeout the timeout
3231     * @return the proxy's last response if unsuccessful, or null if
3232     *         tunnel successfuly established
3233     * @exception IOException
3234     * @exception ModuleException
3235     */
3236    private Response enableSSLTunneling(Socket[] sock, Request req, int timeout)
3237    throws IOException, ModuleException
3238    {
3239  // copy User-Agent and Proxy-Auth headers from request
3240
3241  Vector hdrs = new Vector();
3242  for (int idx=0; idx<req.getHeaders().length; idx++)
3243  {
3244      String name = req.getHeaders()[idx].getName();
3245      if (name.equalsIgnoreCase("User-Agent")  ||
3246    name.equalsIgnoreCase("Proxy-Authorization"))
3247        hdrs.addElement(req.getHeaders()[idx]);
3248  }
3249
3250
3251  // create initial CONNECT subrequest
3252
3253  NVPair[] h = new NVPair[hdrs.size()];
3254  hdrs.copyInto(h);
3255  Request connect = new Request(this, "CONNECT", Host+":"+Port, h,
3256              null, null, req.allowUI());
3257  connect.internal_subrequest = true;
3258
3259  ByteArrayOutputStream hdr_buf = new ByteArrayOutputStream(600);
3260  HTTPResponse r = new HTTPResponse(gen_mod_insts(), timeout, connect);
3261
3262
3263  // send and handle CONNECT request until successful or tired
3264
3265  Response resp = null;
3266
3267  while (true)
3268  {
3269      handleRequest(connect, r, resp, true);
3270
3271      hdr_buf.reset();
3272      assembleHeaders(connect, hdr_buf);
3273
3274      if (DebugConn)
3275      {
3276    System.err.println("Conn:  Sending SSL-Tunneling Subrequest: ");
3277    System.err.println();
3278    hdr_buf.writeTo(System.err);
3279      }
3280
3281
3282      // send CONNECT
3283
3284      hdr_buf.writeTo(sock[0].getOutputStream());
3285
3286
3287      // return if successful
3288
3289      resp = new Response(connect, sock[0].getInputStream());
3290      if (resp.getStatusCode() == 200)  return null;
3291
3292
3293      // failed!
3294
3295      // make life easy: read data and close socket
3296
3297      try
3298    { resp.getData(); }
3299      catch (IOException ioe)
3300    { }
3301      try
3302    { sock[0].close(); }
3303      catch (IOException ioe)
3304    { }
3305
3306
3307      // handle response
3308
3309      r.set(connect, resp);
3310      if (!r.handleResponse())  return resp;
3311
3312      sock[0] = getSocket(timeout);
3313  }
3314    }
3315
3316    /**
3317     * Check whether the name in the certificate matches the host
3318     * we're talking to.
3319     */
3320    private static void checkCert(X509Certificate cert, String host)
3321      throws IOException
3322    {
3323  String name;
3324  try
3325  {
3326      name = ((sun.security.x509.X500Name) cert.getSubjectDN()).
3327      getCommonName().toLowerCase();
3328  }
3329  catch (Throwable t)
3330      { return; }   // Oh well, can't check the name in that case
3331
3332  if (name.equals(host))
3333      return;
3334
3335  if (name.charAt(0) == '*'  &&  host.endsWith(name.substring(1)))
3336      return;
3337
3338  throw new SSLException("Name in certificate `" + name + "' does not " +
3339             "match host name `" + host + "'");
3340    }
3341
3342    /**
3343     * This writes out the headers on the <var>hdr_buf</var>. It takes
3344     * special precautions for the following headers:
3345     * <DL>
3346     * <DT>Content-type<DI>This is only written if the request has an entity.
3347     *                     If the request has an entity and no content-type
3348     *                     header was given for the request it defaults to
3349     *                     "application/octet-stream"
3350     * <DT>Content-length<DI>This header is generated if the request has an
3351     *                     entity and the entity isn't being sent with the
3352     *                     Transfer-Encoding "chunked".
3353     * <DT>User-Agent  <DI>If not present it will be generated with the current
3354     *                     HTTPClient version strings. Otherwise the version
3355     *                     string is appended to the given User-Agent string.
3356     * <DT>Connection  <DI>This header is only written if no proxy is used.
3357     *                     If no connection header is specified and the server
3358     *                     is not known to understand HTTP/1.1 or later then
3359     *                     a "Connection: keep-alive" header is generated.
3360     * <DT>Proxy-Connection<DI>This header is only written if a proxy is used.
3361     *                     If no connection header is specified and the proxy
3362     *                     is not known to understand HTTP/1.1 or later then
3363     *                     a "Proxy-Connection: keep-alive" header is generated.
3364     * <DT>Keep-Alive  <DI>This header is only written if the Connection or
3365     *                     Proxy-Connection header contains the Keep-Alive
3366     *                     token.
3367     * <DT>Expect      <DI>If there is no entity and this header contains the
3368     *                     "100-continue" token then this token is removed.
3369     *                     before writing the header.
3370     * <DT>TE          <DI>If this header does not exist, it is created; else
3371     *                     if the "trailers" token is not specified this token
3372     *                     is added; else the header is not touched.
3373     * </DL>
3374     *
3375     * Furthermore, it escapes various characters in request-URI.
3376     *
3377     * @param req     the Request
3378     * @param hdr_buf the buffer onto which to write the headers
3379     * @return        an array of headers; the first element contains the
3380     *                the value of the Connection or Proxy-Connectin header,
3381     *                the second element the value of the Expect header.
3382     * @exception IOException if writing on <var>hdr_buf</var> generates an
3383     *                        an IOException, or if an error occurs during
3384     *                        parsing of a header
3385     */
3386    private String[] assembleHeaders(Request req,
3387             ByteArrayOutputStream hdr_buf)
3388    throws IOException
3389    {
3390  DataOutputStream dataout  = new DataOutputStream(hdr_buf);
3391  String[]         con_hdrs = { "", "" };
3392  NVPair[]         hdrs     = req.getHeaders();
3393
3394
3395
3396  // Generate request line and Host header
3397
3398  String file = Util.escapeUnsafeChars(req.getRequestURI());
3399  if (Proxy_Host != null  &&  Protocol != HTTPS  &&  !file.equals("*"))
3400      dataout.writeBytes(req.getMethod() + " http://" + Host + ":" + Port+
3401             file + " " + RequestProtocolVersion + "\r\n");
3402  else
3403      dataout.writeBytes(req.getMethod() + " " + file + " " +
3404             RequestProtocolVersion + "\r\n");
3405
3406  if (Port != 80)
3407      dataout.writeBytes("Host: " + Host + ":" + Port + "\r\n");
3408  else    // Netscape-Enterprise has some bugs...
3409      dataout.writeBytes("Host: " + Host + "\r\n");
3410
3411
3412  // remember various headers
3413
3414  int ct_idx = -1,
3415      ua_idx = -1,
3416      co_idx = -1,
3417      pc_idx = -1,
3418      ka_idx = -1,
3419      ex_idx = -1,
3420      te_idx = -1,
3421      tc_idx = -1,
3422      ug_idx = -1;
3423  for (int idx=0; idx<hdrs.length; idx++)
3424  {
3425      String name = hdrs[idx].getName().trim();
3426      if (name.equalsIgnoreCase("Content-Type"))           ct_idx = idx;
3427      else if (name.equalsIgnoreCase("User-Agent"))        ua_idx = idx;
3428      else if (name.equalsIgnoreCase("Connection"))        co_idx = idx;
3429      else if (name.equalsIgnoreCase("Proxy-Connection"))  pc_idx = idx;
3430      else if (name.equalsIgnoreCase("Keep-Alive"))        ka_idx = idx;
3431      else if (name.equalsIgnoreCase("Expect"))            ex_idx = idx;
3432      else if (name.equalsIgnoreCase("TE"))                te_idx = idx;
3433      else if (name.equalsIgnoreCase("Transfer-Encoding")) tc_idx = idx;
3434      else if (name.equalsIgnoreCase("Upgrade"))           ug_idx = idx;
3435  }
3436
3437
3438  /*
3439   * What follows is the setup for persistent connections. We default
3440   * to doing persistent connections for both HTTP/1.0 and HTTP/1.1,
3441   * unless we're using a proxy server and HTTP/1.0 in which case we
3442   * must make sure we don't do persistence (because of the problem of
3443   * 1.0 proxies blindly passing the Connection header on).
3444   *
3445   * Note: there is a "Proxy-Connection" header for use with proxies.
3446   * This however is only understood by Netscape and Netapp caches.
3447   * Furthermore, it suffers from the same problem as the Connection
3448   * header in HTTP/1.0 except that at least two proxies must be
3449   * involved. But I've taken the risk now and decided to send the
3450   * Proxy-Connection header. If I get complaints I'll remove it again.
3451   *
3452   * In any case, with this header we can now modify the above to send
3453   * the Proxy-Connection header whenever we wouldn't send the normal
3454   * Connection header.
3455   */
3456
3457  String co_hdr = null;
3458  if (!(ServProtVersKnown  &&  ServerProtocolVersion >= HTTP_1_1  &&
3459        co_idx == -1))
3460  {
3461      if (co_idx == -1)
3462      {      // no connection header given by user
3463    co_hdr = "Keep-Alive";
3464    con_hdrs[0] = "Keep-Alive";
3465      }
3466      else
3467      {
3468    con_hdrs[0] = hdrs[co_idx].getValue().trim();
3469    co_hdr = con_hdrs[0];
3470      }
3471
3472      try
3473      {
3474    if (ka_idx != -1  &&
3475        Util.hasToken(con_hdrs[0], "keep-alive"))
3476        dataout.writeBytes("Keep-Alive: " +
3477          hdrs[ka_idx].getValue().trim() + "\r\n");
3478      }
3479      catch (ParseException pe)
3480      {
3481    throw new IOException(pe.toString());
3482      }
3483  }
3484
3485  if ((Proxy_Host != null  &&  Protocol != HTTPS)  &&
3486      !(ServProtVersKnown  &&  ServerProtocolVersion >= HTTP_1_1))
3487  {
3488      if (co_hdr != null)
3489      {
3490    dataout.writeBytes("Proxy-Connection: ");
3491    dataout.writeBytes(co_hdr);
3492    dataout.writeBytes("\r\n");
3493    co_hdr = null;
3494      }
3495  }
3496
3497  if (co_hdr != null)
3498  {
3499      try
3500      {
3501    if (!Util.hasToken(co_hdr, "TE"))
3502        co_hdr += ", TE";
3503      }
3504      catch (ParseException pe)
3505    { throw new IOException(pe.toString()); }
3506  }
3507  else
3508      co_hdr = "TE";
3509
3510  if (ug_idx != -1)
3511      co_hdr += ", Upgrade";
3512
3513  if (co_hdr != null)
3514  {
3515      dataout.writeBytes("Connection: ");
3516      dataout.writeBytes(co_hdr);
3517      dataout.writeBytes("\r\n");
3518  }
3519
3520
3521
3522  // handle TE header
3523
3524  if (te_idx != -1)
3525  {
3526      dataout.writeBytes("TE: ");
3527      Vector pte;
3528      try
3529    { pte = Util.parseHeader(hdrs[te_idx].getValue()); }
3530      catch (ParseException pe)
3531    { throw new IOException(pe.toString()); }
3532
3533      if (!pte.contains(new HttpHeaderElement("trailers")))
3534    dataout.writeBytes("trailers, ");
3535
3536      dataout.writeBytes(hdrs[te_idx].getValue().trim() + "\r\n");
3537  }
3538  else
3539      dataout.writeBytes("TE: trailers\r\n");
3540
3541
3542  // User-Agent
3543
3544  if (ua_idx != -1)
3545      dataout.writeBytes("User-Agent: " + hdrs[ua_idx].getValue().trim() + " "
3546             + version + "\r\n");
3547  else
3548      dataout.writeBytes("User-Agent: " + version + "\r\n");
3549
3550
3551  // Write out any headers left
3552
3553  for (int idx=0; idx<hdrs.length; idx++)
3554  {
3555      if (idx != ct_idx  &&  idx != ua_idx  &&  idx != co_idx  &&
3556    idx != pc_idx  &&  idx != ka_idx  &&  idx != ex_idx  &&
3557    idx != te_idx)
3558    dataout.writeBytes(hdrs[idx].getName().trim() + ": " +
3559           hdrs[idx].getValue().trim() + "\r\n");
3560  }
3561
3562
3563  // Handle Content-type, Content-length and Expect headers
3564
3565  if (req.getData() != null  ||  req.getStream() != null)
3566  {
3567      dataout.writeBytes("Content-type: ");
3568      if (ct_idx != -1)
3569    dataout.writeBytes(hdrs[ct_idx].getValue().trim());
3570      else
3571    dataout.writeBytes("application/octet-stream");
3572      dataout.writeBytes("\r\n");
3573
3574      if (req.getData() != null)
3575    dataout.writeBytes("Content-length: " +req.getData().length +
3576           "\r\n");
3577      else if (req.getStream().getLength() != -1  &&  tc_idx == -1)
3578    dataout.writeBytes("Content-length: " +
3579           req.getStream().getLength() + "\r\n");
3580
3581      if (ex_idx != -1)
3582      {
3583    con_hdrs[1] = hdrs[ex_idx].getValue().trim();
3584    dataout.writeBytes("Expect: " + con_hdrs[1] + "\r\n");
3585      }
3586  }
3587  else if (ex_idx != -1)
3588  {
3589      Vector expect_tokens;
3590      try
3591    { expect_tokens = Util.parseHeader(hdrs[ex_idx].getValue()); }
3592      catch (ParseException pe)
3593    { throw new IOException(pe.toString()); }
3594
3595
3596      // remove any 100-continue tokens
3597
3598      HttpHeaderElement cont = new HttpHeaderElement("100-continue");
3599      while (expect_tokens.removeElement(cont)) ;
3600
3601
3602      // write out header if any tokens left
3603
3604      if (!expect_tokens.isEmpty())
3605      {
3606    con_hdrs[1] = Util.assembleHeader(expect_tokens);
3607    dataout.writeBytes("Expect: " + con_hdrs[1] + "\r\n");
3608      }
3609  }
3610
3611  dataout.writeBytes("\r\n");    // end of header
3612
3613  return con_hdrs;
3614    }
3615
3616
3617    /**
3618     * The very first request is special in that we use it to figure out the
3619     * protocol version the server (or proxy) is compliant with.
3620     *
3621     * @return true if all went fine, false if the request needs to be resent
3622     * @exception IOException if any exception is thrown by the response
3623     */
3624    boolean handleFirstRequest(Request req, Response resp)  throws IOException
3625    {
3626  // read response headers to get protocol version used by
3627  // the server.
3628
3629  ServerProtocolVersion = String2ProtVers(resp.getVersion());
3630  ServProtVersKnown = true;
3631
3632  /* We need to treat connections through proxies specially, because
3633   * many HTTP/1.0 proxies do not downgrade an HTTP/1.1 response
3634   * version to HTTP/1.0 (i.e. when we are talking to an HTTP/1.1
3635   * server through an HTTP/1.0 proxy we are mislead to thinking we're
3636   * talking to an HTTP/1.1 proxy). We use the absence of the Via
3637   * header to detect whether we're talking to an HTTP/1.0 proxy.
3638   * However, this only works when the chain contains only HTTP/1.0
3639   * proxies; if you have <client - 1.0 proxy - 1.1 proxy - server>
3640   * then this will fail too. Unfortunately there seems to be no way
3641   * to reliably detect broken HTTP/1.0 proxies...
3642   */
3643  if ((Proxy_Host != null  &&  Protocol != HTTPS)  &&
3644      resp.getHeader("Via") == null)
3645      ServerProtocolVersion = HTTP_1_0;
3646
3647  if (DebugConn)
3648      System.err.println("Conn:  Protocol Version established: " +
3649             ProtVers2String(ServerProtocolVersion));
3650
3651
3652  // some (buggy) servers return an error status if they get a
3653  // version they don't comprehend
3654
3655  if (ServerProtocolVersion == HTTP_1_0  &&
3656      (resp.getStatusCode() == 400  ||  resp.getStatusCode() == 500))
3657  {
3658      input_demux.markForClose(resp);
3659      input_demux = null;
3660      RequestProtocolVersion = "HTTP/1.0";
3661      return false;
3662  }
3663
3664  return true;
3665    }
3666
3667
3668    private void determineKeepAlive(Response resp)  throws IOException
3669    {
3670  // try and determine if this server does keep-alives
3671
3672  String con;
3673
3674  try
3675  {
3676      if (ServerProtocolVersion >= HTTP_1_1  ||
3677    (
3678     (
3679      ((Proxy_Host == null  ||  Protocol == HTTPS)  &&
3680       (con = resp.getHeader("Connection")) != null)
3681      ||
3682      ((Proxy_Host != null  &&  Protocol != HTTPS)  &&
3683       (con = resp.getHeader("Proxy-Connection")) != null)
3684     )  &&
3685     Util.hasToken(con, "keep-alive")
3686    )
3687         )
3688      {
3689    DoesKeepAlive = true;
3690
3691    if (DebugConn)
3692        System.err.println("Conn:  Keep-Alive enabled");
3693
3694    KeepAliveUnknown = false;
3695      }
3696      else if (resp.getStatusCode() < 400)
3697    KeepAliveUnknown = false;
3698
3699
3700      // get maximum number of requests
3701
3702      if (DoesKeepAlive  &&  ServerProtocolVersion == HTTP_1_0  &&
3703    (con = resp.getHeader("Keep-Alive")) != null)
3704      {
3705    HttpHeaderElement max =
3706        Util.getElement(Util.parseHeader(con), "max");
3707    if (max != null  &&  max.getValue() != null)
3708    {
3709        KeepAliveReqMax  = Integer.parseInt(max.getValue());
3710        KeepAliveReqLeft = KeepAliveReqMax;
3711
3712        if (DebugConn)
3713      System.err.println("Conn:  Max Keep-Alive requests: "+
3714             KeepAliveReqMax);
3715    }
3716      }
3717  }
3718  catch (ParseException pe) { }
3719  catch (NumberFormatException nfe) { }
3720  catch (ClassCastException cce) { }
3721    }
3722
3723
3724    synchronized void outputFinished()
3725    {
3726  output_finished = true;
3727  notify();
3728    }
3729
3730
3731    synchronized void closeDemux(IOException ioe, boolean was_reset)
3732    {
3733  if (input_demux != null)  input_demux.close(ioe, was_reset);
3734
3735  early_stall = null;
3736  late_stall  = null;
3737  prev_resp   = null;
3738    }
3739
3740
3741    final static String ProtVers2String(int prot_vers)
3742    {
3743  return "HTTP/" + (prot_vers >>> 16) + "." + (prot_vers & 0xFFFF);
3744    }
3745
3746    final static int String2ProtVers(String prot_vers)
3747    {
3748  String vers = prot_vers.substring(5);
3749  int    dot  = vers.indexOf('.');
3750  return  Integer.parseInt(vers.substring(0, dot)) << 16 |
3751    Integer.parseInt(vers.substring(dot+1));
3752    }
3753
3754
3755    /**
3756     * Generates a string of the form protocol://host.domain:port .
3757     *
3758     * @return the string
3759     */
3760    public String toString()
3761    {
3762  return getProtocol() + "://" + getHost() +
3763      (getPort() != URI.defaultPort(getProtocol()) ? ":" + getPort() : "");
3764    }
3765
3766
3767    private class EstablishConnection extends Thread
3768    {
3769  String      actual_host;
3770  int         actual_port;
3771  IOException exception;
3772  Socket      sock;
3773  SocksClient Socks_client;
3774  boolean     close;
3775
3776
3777  EstablishConnection(String host, int port, SocksClient socks)
3778  {
3779      super("EstablishConnection (" + host + ":" + port + ")");
3780      try { setDaemon(true); }
3781      catch (SecurityException se) { }        // Oh well...
3782
3783      actual_host  = host;
3784      actual_port  = port;
3785      Socks_client = socks;
3786
3787      exception = null;
3788      sock      = null;
3789      close     = false;
3790  }
3791
3792
3793  public void run()
3794  {
3795      try
3796      {
3797    if (Socks_client != null)
3798        sock = Socks_client.getSocket(actual_host, actual_port);
3799    else
3800    {
3801        // try all A records
3802        InetAddress[] addr_list = InetAddress.getAllByName(actual_host);
3803        for (int idx=0; idx<addr_list.length; idx++)
3804        {
3805      try
3806      {
3807          sock = new Socket(addr_list[idx], actual_port);
3808          break;    // success
3809      }
3810      catch (SocketException se)  // should be NoRouteToHostException
3811      {
3812          if (idx == addr_list.length-1  ||  close)
3813        throw se;  // we tried them all
3814      }
3815        }
3816    }
3817      }
3818      catch (IOException ioe)
3819      {
3820    exception = ioe;
3821      }
3822
3823      if (close  &&  sock != null)
3824      {
3825    try
3826        { sock.close(); }
3827    catch (IOException ioe)
3828        { }
3829    sock = null;
3830      }
3831  }
3832
3833
3834  IOException getException()
3835  {
3836      return exception;
3837  }
3838
3839
3840  Socket getSocket()
3841  {
3842      return sock;
3843  }
3844
3845
3846  void forget()
3847  {
3848      close = true;
3849  }
3850    }
3851
3852
3853    /**
3854     * M$ has yet another bug in their WinSock: if you try to write too much
3855     * data at once it'll hang itself. This filter therefore splits big writes
3856     * up into multiple writes of at most 20K.
3857     */
3858    private class MSLargeWritesBugStream extends FilterOutputStream
3859    {
3860  private final int CHUNK_SIZE = 20000;
3861
3862  MSLargeWritesBugStream(OutputStream os)
3863  {
3864      super(os);
3865  }
3866
3867  public void write(byte[] b, int off, int len)  throws IOException
3868  {
3869      while (len > CHUNK_SIZE)
3870      {
3871    out.write(b, off, CHUNK_SIZE);
3872    off += CHUNK_SIZE;
3873    len -= CHUNK_SIZE;
3874      }
3875      out.write(b, off, len);
3876  }
3877    }
3878}
3879