History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/net/www/http/HttpClient.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/net/www/http/HttpClient.java+939−0
@@ -0,0 +1,939 @@
1/*
2 * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.net.www.http;
27
28import java.io.*;
29import java.net.*;
30import java.util.Locale;
31import sun.net.NetworkClient;
32import sun.net.ProgressSource;
33import sun.net.www.MessageHeader;
34import sun.net.www.HeaderParser;
35import sun.net.www.MeteredStream;
36import sun.net.www.ParseUtil;
37import sun.net.www.protocol.http.HttpURLConnection;
38import sun.util.logging.PlatformLogger;
39
40/**
41 * @author Herb Jellinek
42 * @author Dave Brown
43 */
44public class HttpClient extends NetworkClient {
45 // whether this httpclient comes from the cache
46 protected boolean cachedHttpClient = false;
47
48 private boolean inCache;
49
50 protected CookieHandler cookieHandler;
51
52 // Http requests we send
53 MessageHeader requests;
54
55 // Http data we send with the headers
56 PosterOutputStream poster = null;
57
58 // true if we are in streaming mode (fixed length or chunked)
59 boolean streaming;
60
61 // if we've had one io error
62 boolean failedOnce = false;
63
64 /** Response code for CONTINUE */
65 private boolean ignoreContinue = true;
66 private static final int HTTP_CONTINUE = 100;
67
68 /** Default port number for http daemons. REMIND: make these private */
69 static final int httpPortNumber = 80;
70
71 /** return default port number (subclasses may override) */
72 protected int getDefaultPort () { return httpPortNumber; }
73
74 static private int getDefaultPort(String proto) {
75 if ("http".equalsIgnoreCase(proto))
76 return 80;
77 if ("https".equalsIgnoreCase(proto))
78 return 443;
79 return -1;
80 }
81
82 /* All proxying (generic as well as instance-specific) may be
83 * disabled through use of this flag
84 */
85 protected boolean proxyDisabled;
86
87 // are we using proxy in this instance?
88 public boolean usingProxy = false;
89 // target host, port for the URL
90 protected String host;
91 protected int port;
92
93 /* where we cache currently open, persistent connections */
94 protected static KeepAliveCache kac = new KeepAliveCache();
95
96 private static boolean keepAliveProp = true;
97
98 // retryPostProp is true by default so as to preserve behavior
99 // from previous releases.
100 private static boolean retryPostProp = true;
101
102 volatile boolean keepingAlive = false; /* this is a keep-alive connection */
103 int keepAliveConnections = -1; /* number of keep-alives left */
104
105 /**Idle timeout value, in milliseconds. Zero means infinity,
106 * iff keepingAlive=true.
107 * Unfortunately, we can't always believe this one. If I'm connected
108 * through a Netscape proxy to a server that sent me a keep-alive
109 * time of 15 sec, the proxy unilaterally terminates my connection
110 * after 5 sec. So we have to hard code our effective timeout to
111 * 4 sec for the case where we're using a proxy. *SIGH*
112 */
113 int keepAliveTimeout = 0;
114
115 /** whether the response is to be cached */
116 private CacheRequest cacheRequest = null;
117
118 /** Url being fetched. */
119 protected URL url;
120
121 /* if set, the client will be reused and must not be put in cache */
122 public boolean reuse = false;
123
124 // Traffic capture tool, if configured. See HttpCapture class for info
125 private HttpCapture capture = null;
126
127 /**
128 * A NOP method kept for backwards binary compatibility
129 * @deprecated -- system properties are no longer cached.
130 */
131 @Deprecated
132 public static synchronized void resetProperties() {
133 }
134
135 int getKeepAliveTimeout() {
136 return keepAliveTimeout;
137 }
138
139 static {
140 String keepAlive = java.security.AccessController.doPrivileged(
141 new sun.security.action.GetPropertyAction("http.keepAlive"));
142
143 String retryPost = java.security.AccessController.doPrivileged(
144 new sun.security.action.GetPropertyAction("sun.net.http.retryPost"));
145
146 if (keepAlive != null) {
147 keepAliveProp = Boolean.valueOf(keepAlive).booleanValue();
148 } else {
149 keepAliveProp = true;
150 }
151
152 if (retryPost != null) {
153 retryPostProp = Boolean.valueOf(retryPost).booleanValue();
154 } else
155 retryPostProp = true;
156
157 }
158
159 /**
160 * @return true iff http keep alive is set (i.e. enabled). Defaults
161 * to true if the system property http.keepAlive isn't set.
162 */
163 public boolean getHttpKeepAliveSet() {
164 return keepAliveProp;
165 }
166
167
168 protected HttpClient() {
169 }
170
171 private HttpClient(URL url)
172 throws IOException {
173 this(url, (String)null, -1, false);
174 }
175
176 protected HttpClient(URL url,
177 boolean proxyDisabled) throws IOException {
178 this(url, null, -1, proxyDisabled);
179 }
180
181 /* This package-only CTOR should only be used for FTP piggy-backed on HTTP
182 * HTTP URL's that use this won't take advantage of keep-alive.
183 * Additionally, this constructor may be used as a last resort when the
184 * first HttpClient gotten through New() failed (probably b/c of a
185 * Keep-Alive mismatch).
186 *
187 * XXX That documentation is wrong ... it's not package-private any more
188 */
189 public HttpClient(URL url, String proxyHost, int proxyPort)
190 throws IOException {
191 this(url, proxyHost, proxyPort, false);
192 }
193
194 protected HttpClient(URL url, Proxy p, int to) throws IOException {
195 proxy = (p == null) ? Proxy.NO_PROXY : p;
196 this.host = url.getHost();
197 this.url = url;
198 port = url.getPort();
199 if (port == -1) {
200 port = getDefaultPort();
201 }
202 setConnectTimeout(to);
203
204 // get the cookieHandler if there is any
205 cookieHandler = java.security.AccessController.doPrivileged(
206 new java.security.PrivilegedAction<CookieHandler>() {
207 public CookieHandler run() {
208 return CookieHandler.getDefault();
209 }
210 });
211
212 capture = HttpCapture.getCapture(url);
213 openServer();
214 }
215
216 static protected Proxy newHttpProxy(String proxyHost, int proxyPort,
217 String proto) {
218 if (proxyHost == null || proto == null)
219 return Proxy.NO_PROXY;
220 int pport = proxyPort < 0 ? getDefaultPort(proto) : proxyPort;
221 InetSocketAddress saddr = InetSocketAddress.createUnresolved(proxyHost, pport);
222 return new Proxy(Proxy.Type.HTTP, saddr);
223 }
224
225 /*
226 * This constructor gives "ultimate" flexibility, including the ability
227 * to bypass implicit proxying. Sometimes we need to be using tunneling
228 * (transport or network level) instead of proxying (application level),
229 * for example when we don't want the application level data to become
230 * visible to third parties.
231 *
232 * @param url the URL to which we're connecting
233 * @param proxy proxy to use for this URL (e.g. forwarding)
234 * @param proxyPort proxy port to use for this URL
235 * @param proxyDisabled true to disable default proxying
236 */
237 private HttpClient(URL url, String proxyHost, int proxyPort,
238 boolean proxyDisabled)
239 throws IOException {
240 this(url, proxyDisabled ? Proxy.NO_PROXY :
241 newHttpProxy(proxyHost, proxyPort, "http"), -1);
242 }
243
244 public HttpClient(URL url, String proxyHost, int proxyPort,
245 boolean proxyDisabled, int to)
246 throws IOException {
247 this(url, proxyDisabled ? Proxy.NO_PROXY :
248 newHttpProxy(proxyHost, proxyPort, "http"), to);
249 }
250
251 /* This class has no public constructor for HTTP. This method is used to
252 * get an HttpClient to the specifed URL. If there's currently an
253 * active HttpClient to that server/port, you'll get that one.
254 */
255 public static HttpClient New(URL url)
256 throws IOException {
257 return HttpClient.New(url, Proxy.NO_PROXY, -1, true);
258 }
259
260 public static HttpClient New(URL url, boolean useCache)
261 throws IOException {
262 return HttpClient.New(url, Proxy.NO_PROXY, -1, useCache);
263 }
264
265 public static HttpClient New(URL url, Proxy p, int to, boolean useCache)
266 throws IOException {
267 if (p == null) {
268 p = Proxy.NO_PROXY;
269 }
270 HttpClient ret = null;
271 /* see if one's already around */
272 if (useCache) {
273 ret = kac.get(url, null);
274 if (ret != null) {
275 if ((ret.proxy != null && ret.proxy.equals(p)) ||
276 (ret.proxy == null && p == null)) {
277 synchronized (ret) {
278 ret.cachedHttpClient = true;
279 assert ret.inCache;
280 ret.inCache = false;
281 PlatformLogger logger = HttpURLConnection.getHttpLogger();
282 if (logger.isLoggable(PlatformLogger.FINEST)) {
283 logger.finest("KeepAlive stream retrieved from the cache, " + ret);
284 }
285 }
286 } else {
287 // We cannot return this connection to the cache as it's
288 // KeepAliveTimeout will get reset. We simply close the connection.
289 // This should be fine as it is very rare that a connection
290 // to the same host will not use the same proxy.
291 synchronized(ret) {
292 ret.inCache = false;
293 ret.closeServer();
294 }
295 ret = null;
296 }
297 }
298 }
299 if (ret == null) {
300 ret = new HttpClient(url, p, to);
301 } else {
302 SecurityManager security = System.getSecurityManager();
303 if (security != null) {
304 if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) {
305 security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());
306 } else {
307 security.checkConnect(url.getHost(), url.getPort());
308 }
309 }
310 ret.url = url;
311 }
312 return ret;
313 }
314
315 public static HttpClient New(URL url, Proxy p, int to) throws IOException {
316 return New(url, p, to, true);
317 }
318
319 public static HttpClient New(URL url, String proxyHost, int proxyPort,
320 boolean useCache)
321 throws IOException {
322 return New(url, newHttpProxy(proxyHost, proxyPort, "http"), -1, useCache);
323 }
324
325 public static HttpClient New(URL url, String proxyHost, int proxyPort,
326 boolean useCache, int to)
327 throws IOException {
328 return New(url, newHttpProxy(proxyHost, proxyPort, "http"), to, useCache);
329 }
330
331 /* return it to the cache as still usable, if:
332 * 1) It's keeping alive, AND
333 * 2) It still has some connections left, AND
334 * 3) It hasn't had a error (PrintStream.checkError())
335 * 4) It hasn't timed out
336 *
337 * If this client is not keepingAlive, it should have been
338 * removed from the cache in the parseHeaders() method.
339 */
340
341 public void finished() {
342 if (reuse) /* will be reused */
343 return;
344 keepAliveConnections--;
345 poster = null;
346 if (keepAliveConnections > 0 && isKeepingAlive() &&
347 !(serverOutput.checkError())) {
348 /* This connection is keepingAlive && still valid.
349 * Return it to the cache.
350 */
351 putInKeepAliveCache();
352 } else {
353 closeServer();
354 }
355 }
356
357 protected synchronized void putInKeepAliveCache() {
358 if (inCache) {
359 assert false : "Duplicate put to keep alive cache";
360 return;
361 }
362 inCache = true;
363 kac.put(url, null, this);
364 }
365
366 protected synchronized boolean isInKeepAliveCache() {
367 return inCache;
368 }
369
370 /*
371 * Close an idle connection to this URL (if it exists in the
372 * cache).
373 */
374 public void closeIdleConnection() {
375 HttpClient http = kac.get(url, null);
376 if (http != null) {
377 http.closeServer();
378 }
379 }
380
381 /* We're very particular here about what our InputStream to the server
382 * looks like for reasons that are apparent if you can decipher the
383 * method parseHTTP(). That's why this method is overidden from the
384 * superclass.
385 */
386 @Override
387 public void openServer(String server, int port) throws IOException {
388 serverSocket = doConnect(server, port);
389 try {
390 OutputStream out = serverSocket.getOutputStream();
391 if (capture != null) {
392 out = new HttpCaptureOutputStream(out, capture);
393 }
394 serverOutput = new PrintStream(
395 new BufferedOutputStream(out),
396 false, encoding);
397 } catch (UnsupportedEncodingException e) {
398 throw new InternalError(encoding+" encoding not found");
399 }
400 serverSocket.setTcpNoDelay(true);
401 }
402
403 /*
404 * Returns true if the http request should be tunneled through proxy.
405 * An example where this is the case is Https.
406 */
407 public boolean needsTunneling() {
408 return false;
409 }
410
411 /*
412 * Returns true if this httpclient is from cache
413 */
414 public synchronized boolean isCachedConnection() {
415 return cachedHttpClient;
416 }
417
418 /*
419 * Finish any work left after the socket connection is
420 * established. In the normal http case, it's a NO-OP. Subclass
421 * may need to override this. An example is Https, where for
422 * direct connection to the origin server, ssl handshake needs to
423 * be done; for proxy tunneling, the socket needs to be converted
424 * into an SSL socket before ssl handshake can take place.
425 */
426 public void afterConnect() throws IOException, UnknownHostException {
427 // NO-OP. Needs to be overwritten by HttpsClient
428 }
429
430 /*
431 * call openServer in a privileged block
432 */
433 private synchronized void privilegedOpenServer(final InetSocketAddress server)
434 throws IOException
435 {
436 try {
437 java.security.AccessController.doPrivileged(
438 new java.security.PrivilegedExceptionAction<Void>() {
439 public Void run() throws IOException {
440 openServer(server.getHostString(), server.getPort());
441 return null;
442 }
443 });
444 } catch (java.security.PrivilegedActionException pae) {
445 throw (IOException) pae.getException();
446 }
447 }
448
449 /*
450 * call super.openServer
451 */
452 private void superOpenServer(final String proxyHost,
453 final int proxyPort)
454 throws IOException, UnknownHostException
455 {
456 super.openServer(proxyHost, proxyPort);
457 }
458
459 /*
460 */
461 protected synchronized void openServer() throws IOException {
462
463 SecurityManager security = System.getSecurityManager();
464
465 if (security != null) {
466 security.checkConnect(host, port);
467 }
468
469 if (keepingAlive) { // already opened
470 return;
471 }
472
473 if (url.getProtocol().equals("http") ||
474 url.getProtocol().equals("https") ) {
475
476 if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
477 sun.net.www.URLConnection.setProxiedHost(host);
478 privilegedOpenServer((InetSocketAddress) proxy.address());
479 usingProxy = true;
480 return;
481 } else {
482 // make direct connection
483 openServer(host, port);
484 usingProxy = false;
485 return;
486 }
487
488 } else {
489 /* we're opening some other kind of url, most likely an
490 * ftp url.
491 */
492 if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
493 sun.net.www.URLConnection.setProxiedHost(host);
494 privilegedOpenServer((InetSocketAddress) proxy.address());
495 usingProxy = true;
496 return;
497 } else {
498 // make direct connection
499 super.openServer(host, port);
500 usingProxy = false;
501 return;
502 }
503 }
504 }
505
506 public String getURLFile() throws IOException {
507
508 String fileName = url.getFile();
509 if ((fileName == null) || (fileName.length() == 0))
510 fileName = "/";
511
512 /**
513 * proxyDisabled is set by subclass HttpsClient!
514 */
515 if (usingProxy && !proxyDisabled) {
516 // Do not use URLStreamHandler.toExternalForm as the fragment
517 // should not be part of the RequestURI. It should be an
518 // absolute URI which does not have a fragment part.
519 StringBuffer result = new StringBuffer(128);
520 result.append(url.getProtocol());
521 result.append(":");
522 if (url.getAuthority() != null && url.getAuthority().length() > 0) {
523 result.append("//");
524 result.append(url.getAuthority());
525 }
526 if (url.getPath() != null) {
527 result.append(url.getPath());
528 }
529 if (url.getQuery() != null) {
530 result.append('?');
531 result.append(url.getQuery());
532 }
533
534 fileName = result.toString();
535 }
536 if (fileName.indexOf('\n') == -1)
537 return fileName;
538 else
539 throw new java.net.MalformedURLException("Illegal character in URL");
540 }
541
542 /**
543 * @deprecated
544 */
545 @Deprecated
546 public void writeRequests(MessageHeader head) {
547 requests = head;
548 requests.print(serverOutput);
549 serverOutput.flush();
550 }
551
552 public void writeRequests(MessageHeader head,
553 PosterOutputStream pos) throws IOException {
554 requests = head;
555 requests.print(serverOutput);
556 poster = pos;
557 if (poster != null)
558 poster.writeTo(serverOutput);
559 serverOutput.flush();
560 }
561
562 public void writeRequests(MessageHeader head,
563 PosterOutputStream pos,
564 boolean streaming) throws IOException {
565 this.streaming = streaming;
566 writeRequests(head, pos);
567 }
568
569 /** Parse the first line of the HTTP request. It usually looks
570 something like: "HTTP/1.0 <number> comment\r\n". */
571
572 public boolean parseHTTP(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc)
573 throws IOException {
574 /* If "HTTP/*" is found in the beginning, return true. Let
575 * HttpURLConnection parse the mime header itself.
576 *
577 * If this isn't valid HTTP, then we don't try to parse a header
578 * out of the beginning of the response into the responses,
579 * and instead just queue up the output stream to it's very beginning.
580 * This seems most reasonable, and is what the NN browser does.
581 */
582
583 try {
584 serverInput = serverSocket.getInputStream();
585 if (capture != null) {
586 serverInput = new HttpCaptureInputStream(serverInput, capture);
587 }
588 serverInput = new BufferedInputStream(serverInput);
589 return (parseHTTPHeader(responses, pi, httpuc));
590 } catch (SocketTimeoutException stex) {
591 // We don't want to retry the request when the app. sets a timeout
592 // but don't close the server if timeout while waiting for 100-continue
593 if (ignoreContinue) {
594 closeServer();
595 }
596 throw stex;
597 } catch (IOException e) {
598 closeServer();
599 cachedHttpClient = false;
600 if (!failedOnce && requests != null) {
601 failedOnce = true;
602 if (httpuc.getRequestMethod().equals("POST") && (!retryPostProp || streaming)) {
603 // do not retry the request
604 } else {
605 // try once more
606 openServer();
607 if (needsTunneling()) {
608 httpuc.doTunneling();
609 }
610 afterConnect();
611 writeRequests(requests, poster);
612 return parseHTTP(responses, pi, httpuc);
613 }
614 }
615 throw e;
616 }
617
618 }
619
620 private boolean parseHTTPHeader(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc)
621 throws IOException {
622 /* If "HTTP/*" is found in the beginning, return true. Let
623 * HttpURLConnection parse the mime header itself.
624 *
625 * If this isn't valid HTTP, then we don't try to parse a header
626 * out of the beginning of the response into the responses,
627 * and instead just queue up the output stream to it's very beginning.
628 * This seems most reasonable, and is what the NN browser does.
629 */
630
631 keepAliveConnections = -1;
632 keepAliveTimeout = 0;
633
634 boolean ret = false;
635 byte[] b = new byte[8];
636
637 try {
638 int nread = 0;
639 serverInput.mark(10);
640 while (nread < 8) {
641 int r = serverInput.read(b, nread, 8 - nread);
642 if (r < 0) {
643 break;
644 }
645 nread += r;
646 }
647 String keep=null;
648 ret = b[0] == 'H' && b[1] == 'T'
649 && b[2] == 'T' && b[3] == 'P' && b[4] == '/' &&
650 b[5] == '1' && b[6] == '.';
651 serverInput.reset();
652 if (ret) { // is valid HTTP - response started w/ "HTTP/1."
653 responses.parseHeader(serverInput);
654
655 // we've finished parsing http headers
656 // check if there are any applicable cookies to set (in cache)
657 if (cookieHandler != null) {
658 URI uri = ParseUtil.toURI(url);
659 // NOTE: That cast from Map shouldn't be necessary but
660 // a bug in javac is triggered under certain circumstances
661 // So we do put the cast in as a workaround until
662 // it is resolved.
663 if (uri != null)
664 cookieHandler.put(uri, responses.getHeaders());
665 }
666
667 /* decide if we're keeping alive:
668 * This is a bit tricky. There's a spec, but most current
669 * servers (10/1/96) that support this differ in dialects.
670 * If the server/client misunderstand each other, the
671 * protocol should fall back onto HTTP/1.0, no keep-alive.
672 */
673 if (usingProxy) { // not likely a proxy will return this
674 keep = responses.findValue("Proxy-Connection");
675 }
676 if (keep == null) {
677 keep = responses.findValue("Connection");
678 }
679 if (keep != null && keep.toLowerCase(Locale.US).equals("keep-alive")) {
680 /* some servers, notably Apache1.1, send something like:
681 * "Keep-Alive: timeout=15, max=1" which we should respect.
682 */
683 HeaderParser p = new HeaderParser(
684 responses.findValue("Keep-Alive"));
685 if (p != null) {
686 /* default should be larger in case of proxy */
687 keepAliveConnections = p.findInt("max", usingProxy?50:5);
688 keepAliveTimeout = p.findInt("timeout", usingProxy?60:5);
689 }
690 } else if (b[7] != '0') {
691 /*
692 * We're talking 1.1 or later. Keep persistent until
693 * the server says to close.
694 */
695 if (keep != null) {
696 /*
697 * The only Connection token we understand is close.
698 * Paranoia: if there is any Connection header then
699 * treat as non-persistent.
700 */
701 keepAliveConnections = 1;
702 } else {
703 keepAliveConnections = 5;
704 }
705 }
706 } else if (nread != 8) {
707 if (!failedOnce && requests != null) {
708 failedOnce = true;
709 if (httpuc.getRequestMethod().equals("POST") && (!retryPostProp || streaming)) {
710 // do not retry the request
711 } else {
712 closeServer();
713 cachedHttpClient = false;
714 openServer();
715 if (needsTunneling()) {
716 httpuc.doTunneling();
717 }
718 afterConnect();
719 writeRequests(requests, poster);
720 return parseHTTP(responses, pi, httpuc);
721 }
722 }
723 throw new SocketException("Unexpected end of file from server");
724 } else {
725 // we can't vouche for what this is....
726 responses.set("Content-type", "unknown/unknown");
727 }
728 } catch (IOException e) {
729 throw e;
730 }
731
732 int code = -1;
733 try {
734 String resp;
735 resp = responses.getValue(0);
736 /* should have no leading/trailing LWS
737 * expedite the typical case by assuming it has
738 * form "HTTP/1.x <WS> 2XX <mumble>"
739 */
740 int ind;
741 ind = resp.indexOf(' ');
742 while(resp.charAt(ind) == ' ')
743 ind++;
744 code = Integer.parseInt(resp.substring(ind, ind + 3));
745 } catch (Exception e) {}
746
747 if (code == HTTP_CONTINUE && ignoreContinue) {
748 responses.reset();
749 return parseHTTPHeader(responses, pi, httpuc);
750 }
751
752 long cl = -1;
753
754 /*
755 * Set things up to parse the entity body of the reply.
756 * We should be smarter about avoid pointless work when
757 * the HTTP method and response code indicate there will be
758 * no entity body to parse.
759 */
760 String te = responses.findValue("Transfer-Encoding");
761 if (te != null && te.equalsIgnoreCase("chunked")) {
762 serverInput = new ChunkedInputStream(serverInput, this, responses);
763
764 /*
765 * If keep alive not specified then close after the stream
766 * has completed.
767 */
768 if (keepAliveConnections <= 1) {
769 keepAliveConnections = 1;
770 keepingAlive = false;
771 } else {
772 keepingAlive = true;
773 }
774 failedOnce = false;
775 } else {
776
777 /*
778 * If it's a keep alive connection then we will keep
779 * (alive if :-
780 * 1. content-length is specified, or
781 * 2. "Not-Modified" or "No-Content" responses - RFC 2616 states that
782 * 204 or 304 response must not include a message body.
783 */
784 String cls = responses.findValue("content-length");
785 if (cls != null) {
786 try {
787 cl = Long.parseLong(cls);
788 } catch (NumberFormatException e) {
789 cl = -1;
790 }
791 }
792 String requestLine = requests.getKey(0);
793
794 if ((requestLine != null &&
795 (requestLine.startsWith("HEAD"))) ||
796 code == HttpURLConnection.HTTP_NOT_MODIFIED ||
797 code == HttpURLConnection.HTTP_NO_CONTENT) {
798 cl = 0;
799 }
800
801 if (keepAliveConnections > 1 &&
802 (cl >= 0 ||
803 code == HttpURLConnection.HTTP_NOT_MODIFIED ||
804 code == HttpURLConnection.HTTP_NO_CONTENT)) {
805 keepingAlive = true;
806 failedOnce = false;
807 } else if (keepingAlive) {
808 /* Previously we were keeping alive, and now we're not. Remove
809 * this from the cache (but only here, once) - otherwise we get
810 * multiple removes and the cache count gets messed up.
811 */
812 keepingAlive=false;
813 }
814 }
815
816 /* wrap a KeepAliveStream/MeteredStream around it if appropriate */
817
818 if (cl > 0) {
819 // In this case, content length is well known, so it is okay
820 // to wrap the input stream with KeepAliveStream/MeteredStream.
821
822 if (pi != null) {
823 // Progress monitor is enabled
824 pi.setContentType(responses.findValue("content-type"));
825 }
826
827 if (isKeepingAlive()) {
828 // Wrap KeepAliveStream if keep alive is enabled.
829 PlatformLogger logger = HttpURLConnection.getHttpLogger();
830 if (logger.isLoggable(PlatformLogger.FINEST)) {
831 logger.finest("KeepAlive stream used: " + url);
832 }
833 serverInput = new KeepAliveStream(serverInput, pi, cl, this);
834 failedOnce = false;
835 }
836 else {
837 serverInput = new MeteredStream(serverInput, pi, cl);
838 }
839 }
840 else if (cl == -1) {
841 // In this case, content length is unknown - the input
842 // stream would simply be a regular InputStream or
843 // ChunkedInputStream.
844
845 if (pi != null) {
846 // Progress monitoring is enabled.
847
848 pi.setContentType(responses.findValue("content-type"));
849
850 // Wrap MeteredStream for tracking indeterministic
851 // progress, even if the input stream is ChunkedInputStream.
852 serverInput = new MeteredStream(serverInput, pi, cl);
853 }
854 else {
855 // Progress monitoring is disabled, and there is no
856 // need to wrap an unknown length input stream.
857
858 // ** This is an no-op **
859 }
860 }
861 else {
862 if (pi != null)
863 pi.finishTracking();
864 }
865
866 return ret;
867 }
868
869 public synchronized InputStream getInputStream() {
870 return serverInput;
871 }
872
873 public OutputStream getOutputStream() {
874 return serverOutput;
875 }
876
877 @Override
878 public String toString() {
879 return getClass().getName()+"("+url+")";
880 }
881
882 public final boolean isKeepingAlive() {
883 return getHttpKeepAliveSet() && keepingAlive;
884 }
885
886 public void setCacheRequest(CacheRequest cacheRequest) {
887 this.cacheRequest = cacheRequest;
888 }
889
890 CacheRequest getCacheRequest() {
891 return cacheRequest;
892 }
893
894 @Override
895 protected void finalize() throws Throwable {
896 // This should do nothing. The stream finalizer will
897 // close the fd.
898 }
899
900 public void setDoNotRetry(boolean value) {
901 // failedOnce is used to determine if a request should be retried.
902 failedOnce = value;
903 }
904
905 public void setIgnoreContinue(boolean value) {
906 ignoreContinue = value;
907 }
908
909 /* Use only on connections in error. */
910 @Override
911 public void closeServer() {
912 try {
913 keepingAlive = false;
914 serverSocket.close();
915 } catch (Exception e) {}
916 }
917
918 /**
919 * @return the proxy host being used for this client, or null
920 * if we're not going through a proxy
921 */
922 public String getProxyHostUsed() {
923 if (!usingProxy) {
924 return null;
925 } else {
926 return ((InetSocketAddress)proxy.address()).getHostString();
927 }
928 }
929
930 /**
931 * @return the proxy port being used for this client. Meaningless
932 * if getProxyHostUsed() gives null.
933 */
934 public int getProxyPortUsed() {
935 if (usingProxy)
936 return ((InetSocketAddress)proxy.address()).getPort();
937 return -1;
938 }
939}