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+
26+package sun.net.www.http;
27+
28+import java.io.*;
29+import java.net.*;
30+import java.util.Locale;
31+import sun.net.NetworkClient;
32+import sun.net.ProgressSource;
33+import sun.net.www.MessageHeader;
34+import sun.net.www.HeaderParser;
35+import sun.net.www.MeteredStream;
36+import sun.net.www.ParseUtil;
37+import sun.net.www.protocol.http.HttpURLConnection;
38+import sun.util.logging.PlatformLogger;
39+
40+/**
41+ * @author Herb Jellinek
42+ * @author Dave Brown
43+ */
44+public 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+}