View Javadoc

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