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