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