Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/net/www/http/HttpClient.java
38923 views
/*1* Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.net.www.http;2627import java.io.*;28import java.net.*;29import java.util.Locale;30import sun.net.NetworkClient;31import sun.net.ProgressSource;32import sun.net.www.MessageHeader;33import sun.net.www.HeaderParser;34import sun.net.www.MeteredStream;35import sun.net.www.ParseUtil;36import sun.net.www.protocol.http.HttpURLConnection;37import sun.util.logging.PlatformLogger;38import static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*;3940/**41* @author Herb Jellinek42* @author Dave Brown43*/44public class HttpClient extends NetworkClient {45// whether this httpclient comes from the cache46protected boolean cachedHttpClient = false;4748protected boolean inCache;4950// Http requests we send51MessageHeader requests;5253// Http data we send with the headers54PosterOutputStream poster = null;5556// true if we are in streaming mode (fixed length or chunked)57boolean streaming;5859// if we've had one io error60boolean failedOnce = false;6162/** Response code for CONTINUE */63private boolean ignoreContinue = true;64private static final int HTTP_CONTINUE = 100;6566/** Default port number for http daemons. REMIND: make these private */67static final int httpPortNumber = 80;6869/** return default port number (subclasses may override) */70protected int getDefaultPort () { return httpPortNumber; }7172static private int getDefaultPort(String proto) {73if ("http".equalsIgnoreCase(proto))74return 80;75if ("https".equalsIgnoreCase(proto))76return 443;77return -1;78}7980/* All proxying (generic as well as instance-specific) may be81* disabled through use of this flag82*/83protected boolean proxyDisabled;8485// are we using proxy in this instance?86public boolean usingProxy = false;87// target host, port for the URL88protected String host;89protected int port;9091/* where we cache currently open, persistent connections */92protected static KeepAliveCache kac = new KeepAliveCache();9394private static boolean keepAliveProp = true;9596// retryPostProp is true by default so as to preserve behavior97// from previous releases.98private static boolean retryPostProp = true;99100/* Value of the system property jdk.ntlm.cache;101if false, then NTLM connections will not be cached.102The default value is 'true'. */103private static final boolean cacheNTLMProp;104/* Value of the system property jdk.spnego.cache;105if false, then connections authentified using the Negotiate/Kerberos106scheme will not be cached.107The default value is 'true'. */108private static final boolean cacheSPNEGOProp;109110volatile boolean keepingAlive = false; /* this is a keep-alive connection */111volatile boolean disableKeepAlive;/* keep-alive has been disabled for this112connection - this will be used when113recomputing the value of keepingAlive */114int keepAliveConnections = -1; /* number of keep-alives left */115116/**Idle timeout value, in milliseconds. Zero means infinity,117* iff keepingAlive=true.118* Unfortunately, we can't always believe this one. If I'm connected119* through a Netscape proxy to a server that sent me a keep-alive120* time of 15 sec, the proxy unilaterally terminates my connection121* after 5 sec. So we have to hard code our effective timeout to122* 4 sec for the case where we're using a proxy. *SIGH*123*/124int keepAliveTimeout = 0;125126/** whether the response is to be cached */127private CacheRequest cacheRequest = null;128129/** Url being fetched. */130protected URL url;131132/* if set, the client will be reused and must not be put in cache */133public boolean reuse = false;134135// Traffic capture tool, if configured. See HttpCapture class for info136private HttpCapture capture = null;137138private static final PlatformLogger logger = HttpURLConnection.getHttpLogger();139private static void logFinest(String msg) {140if (logger.isLoggable(PlatformLogger.Level.FINEST)) {141logger.finest(msg);142}143}144145/**146* A NOP method kept for backwards binary compatibility147* @deprecated -- system properties are no longer cached.148*/149@Deprecated150public static synchronized void resetProperties() {151}152153int getKeepAliveTimeout() {154return keepAliveTimeout;155}156157static {158String keepAlive = java.security.AccessController.doPrivileged(159new sun.security.action.GetPropertyAction("http.keepAlive"));160161String retryPost = java.security.AccessController.doPrivileged(162new sun.security.action.GetPropertyAction("sun.net.http.retryPost"));163164String cacheNTLM = java.security.AccessController.doPrivileged(165new sun.security.action.GetPropertyAction("jdk.ntlm.cache"));166167String cacheSPNEGO = java.security.AccessController.doPrivileged(168new sun.security.action.GetPropertyAction("jdk.spnego.cache"));169170if (keepAlive != null) {171keepAliveProp = Boolean.valueOf(keepAlive).booleanValue();172} else {173keepAliveProp = true;174}175176if (retryPost != null) {177retryPostProp = Boolean.valueOf(retryPost).booleanValue();178} else {179retryPostProp = true;180}181182if (cacheNTLM != null) {183cacheNTLMProp = Boolean.parseBoolean(cacheNTLM);184} else {185cacheNTLMProp = true;186}187188if (cacheSPNEGO != null) {189cacheSPNEGOProp = Boolean.parseBoolean(cacheSPNEGO);190} else {191cacheSPNEGOProp = true;192}193}194195/**196* @return true iff http keep alive is set (i.e. enabled). Defaults197* to true if the system property http.keepAlive isn't set.198*/199public boolean getHttpKeepAliveSet() {200return keepAliveProp;201}202203204protected HttpClient() {205}206207private HttpClient(URL url)208throws IOException {209this(url, (String)null, -1, false);210}211212protected HttpClient(URL url,213boolean proxyDisabled) throws IOException {214this(url, null, -1, proxyDisabled);215}216217/* This package-only CTOR should only be used for FTP piggy-backed on HTTP218* HTTP URL's that use this won't take advantage of keep-alive.219* Additionally, this constructor may be used as a last resort when the220* first HttpClient gotten through New() failed (probably b/c of a221* Keep-Alive mismatch).222*223* XXX That documentation is wrong ... it's not package-private any more224*/225public HttpClient(URL url, String proxyHost, int proxyPort)226throws IOException {227this(url, proxyHost, proxyPort, false);228}229230protected HttpClient(URL url, Proxy p, int to) throws IOException {231proxy = (p == null) ? Proxy.NO_PROXY : p;232this.host = url.getHost();233this.url = url;234port = url.getPort();235if (port == -1) {236port = getDefaultPort();237}238setConnectTimeout(to);239240capture = HttpCapture.getCapture(url);241openServer();242}243244static protected Proxy newHttpProxy(String proxyHost, int proxyPort,245String proto) {246if (proxyHost == null || proto == null)247return Proxy.NO_PROXY;248int pport = proxyPort < 0 ? getDefaultPort(proto) : proxyPort;249InetSocketAddress saddr = InetSocketAddress.createUnresolved(proxyHost, pport);250return new Proxy(Proxy.Type.HTTP, saddr);251}252253/*254* This constructor gives "ultimate" flexibility, including the ability255* to bypass implicit proxying. Sometimes we need to be using tunneling256* (transport or network level) instead of proxying (application level),257* for example when we don't want the application level data to become258* visible to third parties.259*260* @param url the URL to which we're connecting261* @param proxy proxy to use for this URL (e.g. forwarding)262* @param proxyPort proxy port to use for this URL263* @param proxyDisabled true to disable default proxying264*/265private HttpClient(URL url, String proxyHost, int proxyPort,266boolean proxyDisabled)267throws IOException {268this(url, proxyDisabled ? Proxy.NO_PROXY :269newHttpProxy(proxyHost, proxyPort, "http"), -1);270}271272public HttpClient(URL url, String proxyHost, int proxyPort,273boolean proxyDisabled, int to)274throws IOException {275this(url, proxyDisabled ? Proxy.NO_PROXY :276newHttpProxy(proxyHost, proxyPort, "http"), to);277}278279/* This class has no public constructor for HTTP. This method is used to280* get an HttpClient to the specified URL. If there's currently an281* active HttpClient to that server/port, you'll get that one.282*/283public static HttpClient New(URL url)284throws IOException {285return HttpClient.New(url, Proxy.NO_PROXY, -1, true, null);286}287288public static HttpClient New(URL url, boolean useCache)289throws IOException {290return HttpClient.New(url, Proxy.NO_PROXY, -1, useCache, null);291}292293public static HttpClient New(URL url, Proxy p, int to, boolean useCache,294HttpURLConnection httpuc) throws IOException295{296if (p == null) {297p = Proxy.NO_PROXY;298}299HttpClient ret = null;300/* see if one's already around */301if (useCache) {302ret = kac.get(url, null);303if (ret != null && httpuc != null &&304httpuc.streaming() &&305httpuc.getRequestMethod() == "POST") {306if (!ret.available()) {307ret.inCache = false;308ret.closeServer();309ret = null;310}311}312313if (ret != null) {314if ((ret.proxy != null && ret.proxy.equals(p)) ||315(ret.proxy == null && p == null)) {316synchronized (ret) {317ret.cachedHttpClient = true;318assert ret.inCache;319ret.inCache = false;320if (httpuc != null && ret.needsTunneling())321httpuc.setTunnelState(TUNNELING);322logFinest("KeepAlive stream retrieved from the cache, " + ret);323}324} else {325// We cannot return this connection to the cache as it's326// KeepAliveTimeout will get reset. We simply close the connection.327// This should be fine as it is very rare that a connection328// to the same host will not use the same proxy.329synchronized(ret) {330ret.inCache = false;331ret.closeServer();332}333ret = null;334}335}336}337if (ret == null) {338ret = new HttpClient(url, p, to);339} else {340SecurityManager security = System.getSecurityManager();341if (security != null) {342if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) {343security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());344} else {345security.checkConnect(url.getHost(), url.getPort());346}347}348ret.url = url;349}350return ret;351}352353public static HttpClient New(URL url, Proxy p, int to,354HttpURLConnection httpuc) throws IOException355{356return New(url, p, to, true, httpuc);357}358359public static HttpClient New(URL url, String proxyHost, int proxyPort,360boolean useCache)361throws IOException {362return New(url, newHttpProxy(proxyHost, proxyPort, "http"),363-1, useCache, null);364}365366public static HttpClient New(URL url, String proxyHost, int proxyPort,367boolean useCache, int to,368HttpURLConnection httpuc)369throws IOException {370return New(url, newHttpProxy(proxyHost, proxyPort, "http"),371to, useCache, httpuc);372}373374/* return it to the cache as still usable, if:375* 1) It's keeping alive, AND376* 2) It still has some connections left, AND377* 3) It hasn't had a error (PrintStream.checkError())378* 4) It hasn't timed out379*380* If this client is not keepingAlive, it should have been381* removed from the cache in the parseHeaders() method.382*/383384public void finished() {385if (reuse) /* will be reused */386return;387keepAliveConnections--;388poster = null;389if (keepAliveConnections > 0 && isKeepingAlive() &&390!(serverOutput.checkError())) {391/* This connection is keepingAlive && still valid.392* Return it to the cache.393*/394putInKeepAliveCache();395} else {396closeServer();397}398}399400protected synchronized boolean available() {401boolean available = true;402int old = -1;403404try {405try {406old = serverSocket.getSoTimeout();407serverSocket.setSoTimeout(1);408BufferedInputStream tmpbuf =409new BufferedInputStream(serverSocket.getInputStream());410int r = tmpbuf.read();411if (r == -1) {412logFinest("HttpClient.available(): " +413"read returned -1: not available");414available = false;415}416} catch (SocketTimeoutException e) {417logFinest("HttpClient.available(): " +418"SocketTimeout: its available");419} finally {420if (old != -1)421serverSocket.setSoTimeout(old);422}423} catch (IOException e) {424logFinest("HttpClient.available(): " +425"SocketException: not available");426available = false;427}428return available;429}430431protected synchronized void putInKeepAliveCache() {432if (inCache) {433assert false : "Duplicate put to keep alive cache";434return;435}436inCache = true;437kac.put(url, null, this);438}439440protected synchronized boolean isInKeepAliveCache() {441return inCache;442}443444/*445* Close an idle connection to this URL (if it exists in the446* cache).447*/448public void closeIdleConnection() {449HttpClient http = kac.get(url, null);450if (http != null) {451http.closeServer();452}453}454455/* We're very particular here about what our InputStream to the server456* looks like for reasons that are apparent if you can decipher the457* method parseHTTP(). That's why this method is overidden from the458* superclass.459*/460@Override461public void openServer(String server, int port) throws IOException {462serverSocket = doConnect(server, port);463try {464OutputStream out = serverSocket.getOutputStream();465if (capture != null) {466out = new HttpCaptureOutputStream(out, capture);467}468serverOutput = new PrintStream(469new BufferedOutputStream(out),470false, encoding);471} catch (UnsupportedEncodingException e) {472throw new InternalError(encoding+" encoding not found", e);473}474serverSocket.setTcpNoDelay(true);475}476477/*478* Returns true if the http request should be tunneled through proxy.479* An example where this is the case is Https.480*/481public boolean needsTunneling() {482return false;483}484485/*486* Returns true if this httpclient is from cache487*/488public synchronized boolean isCachedConnection() {489return cachedHttpClient;490}491492/*493* Finish any work left after the socket connection is494* established. In the normal http case, it's a NO-OP. Subclass495* may need to override this. An example is Https, where for496* direct connection to the origin server, ssl handshake needs to497* be done; for proxy tunneling, the socket needs to be converted498* into an SSL socket before ssl handshake can take place.499*/500public void afterConnect() throws IOException, UnknownHostException {501// NO-OP. Needs to be overwritten by HttpsClient502}503504/*505* call openServer in a privileged block506*/507private synchronized void privilegedOpenServer(final InetSocketAddress server)508throws IOException509{510try {511java.security.AccessController.doPrivileged(512new java.security.PrivilegedExceptionAction<Void>() {513public Void run() throws IOException {514openServer(server.getHostString(), server.getPort());515return null;516}517});518} catch (java.security.PrivilegedActionException pae) {519throw (IOException) pae.getException();520}521}522523/*524* call super.openServer525*/526private void superOpenServer(final String proxyHost,527final int proxyPort)528throws IOException, UnknownHostException529{530super.openServer(proxyHost, proxyPort);531}532533/*534*/535protected synchronized void openServer() throws IOException {536537SecurityManager security = System.getSecurityManager();538539if (security != null) {540security.checkConnect(host, port);541}542543if (keepingAlive) { // already opened544return;545}546547if (url.getProtocol().equals("http") ||548url.getProtocol().equals("https") ) {549550if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {551sun.net.www.URLConnection.setProxiedHost(host);552privilegedOpenServer((InetSocketAddress) proxy.address());553usingProxy = true;554return;555} else {556// make direct connection557openServer(host, port);558usingProxy = false;559return;560}561562} else {563/* we're opening some other kind of url, most likely an564* ftp url.565*/566if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {567sun.net.www.URLConnection.setProxiedHost(host);568privilegedOpenServer((InetSocketAddress) proxy.address());569usingProxy = true;570return;571} else {572// make direct connection573super.openServer(host, port);574usingProxy = false;575return;576}577}578}579580public String getURLFile() throws IOException {581582String fileName;583584/**585* proxyDisabled is set by subclass HttpsClient!586*/587if (usingProxy && !proxyDisabled) {588// Do not use URLStreamHandler.toExternalForm as the fragment589// should not be part of the RequestURI. It should be an590// absolute URI which does not have a fragment part.591StringBuffer result = new StringBuffer(128);592result.append(url.getProtocol());593result.append(":");594if (url.getAuthority() != null && url.getAuthority().length() > 0) {595result.append("//");596result.append(url.getAuthority());597}598if (url.getPath() != null) {599result.append(url.getPath());600}601if (url.getQuery() != null) {602result.append('?');603result.append(url.getQuery());604}605606fileName = result.toString();607} else {608fileName = url.getFile();609610if ((fileName == null) || (fileName.length() == 0)) {611fileName = "/";612} else if (fileName.charAt(0) == '?') {613/* HTTP/1.1 spec says in 5.1.2. about Request-URI:614* "Note that the absolute path cannot be empty; if615* none is present in the original URI, it MUST be616* given as "/" (the server root)." So if the file617* name here has only a query string, the path is618* empty and we also have to add a "/".619*/620fileName = "/" + fileName;621}622}623624if (fileName.indexOf('\n') == -1)625return fileName;626else627throw new java.net.MalformedURLException("Illegal character in URL");628}629630/**631* @deprecated632*/633@Deprecated634public void writeRequests(MessageHeader head) {635requests = head;636requests.print(serverOutput);637serverOutput.flush();638}639640public void writeRequests(MessageHeader head,641PosterOutputStream pos) throws IOException {642requests = head;643requests.print(serverOutput);644poster = pos;645if (poster != null)646poster.writeTo(serverOutput);647serverOutput.flush();648}649650public void writeRequests(MessageHeader head,651PosterOutputStream pos,652boolean streaming) throws IOException {653this.streaming = streaming;654writeRequests(head, pos);655}656657/** Parse the first line of the HTTP request. It usually looks658something like: "HTTP/1.0 <number> comment\r\n". */659660public boolean parseHTTP(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc)661throws IOException {662/* If "HTTP/*" is found in the beginning, return true. Let663* HttpURLConnection parse the mime header itself.664*665* If this isn't valid HTTP, then we don't try to parse a header666* out of the beginning of the response into the responses,667* and instead just queue up the output stream to it's very beginning.668* This seems most reasonable, and is what the NN browser does.669*/670671try {672serverInput = serverSocket.getInputStream();673if (capture != null) {674serverInput = new HttpCaptureInputStream(serverInput, capture);675}676serverInput = new BufferedInputStream(serverInput);677return (parseHTTPHeader(responses, pi, httpuc));678} catch (SocketTimeoutException stex) {679// We don't want to retry the request when the app. sets a timeout680// but don't close the server if timeout while waiting for 100-continue681if (ignoreContinue) {682closeServer();683}684throw stex;685} catch (IOException e) {686closeServer();687cachedHttpClient = false;688if (!failedOnce && requests != null) {689failedOnce = true;690if (getRequestMethod().equals("CONNECT")691|| streaming692|| (httpuc.getRequestMethod().equals("POST")693&& !retryPostProp)) {694// do not retry the request695} else {696// try once more697openServer();698if (needsTunneling()) {699MessageHeader origRequests = requests;700httpuc.doTunneling();701requests = origRequests;702}703afterConnect();704writeRequests(requests, poster);705return parseHTTP(responses, pi, httpuc);706}707}708throw e;709}710711}712713private boolean parseHTTPHeader(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc)714throws IOException {715/* If "HTTP/*" is found in the beginning, return true. Let716* HttpURLConnection parse the mime header itself.717*718* If this isn't valid HTTP, then we don't try to parse a header719* out of the beginning of the response into the responses,720* and instead just queue up the output stream to it's very beginning.721* This seems most reasonable, and is what the NN browser does.722*/723724keepAliveConnections = -1;725keepAliveTimeout = 0;726727boolean ret = false;728byte[] b = new byte[8];729730try {731int nread = 0;732serverInput.mark(10);733while (nread < 8) {734int r = serverInput.read(b, nread, 8 - nread);735if (r < 0) {736break;737}738nread += r;739}740String keep=null;741String authenticate=null;742ret = b[0] == 'H' && b[1] == 'T'743&& b[2] == 'T' && b[3] == 'P' && b[4] == '/' &&744b[5] == '1' && b[6] == '.';745serverInput.reset();746if (ret) { // is valid HTTP - response started w/ "HTTP/1."747responses.parseHeader(serverInput);748749// we've finished parsing http headers750// check if there are any applicable cookies to set (in cache)751CookieHandler cookieHandler = httpuc.getCookieHandler();752if (cookieHandler != null) {753URI uri = ParseUtil.toURI(url);754// NOTE: That cast from Map shouldn't be necessary but755// a bug in javac is triggered under certain circumstances756// So we do put the cast in as a workaround until757// it is resolved.758if (uri != null)759cookieHandler.put(uri, responses.getHeaders());760}761762/* decide if we're keeping alive:763* This is a bit tricky. There's a spec, but most current764* servers (10/1/96) that support this differ in dialects.765* If the server/client misunderstand each other, the766* protocol should fall back onto HTTP/1.0, no keep-alive.767*/768if (usingProxy) { // not likely a proxy will return this769keep = responses.findValue("Proxy-Connection");770authenticate = responses.findValue("Proxy-Authenticate");771}772if (keep == null) {773keep = responses.findValue("Connection");774authenticate = responses.findValue("WWW-Authenticate");775}776777// 'disableKeepAlive' starts with the value false.778// It can transition from false to true, but once true779// it stays true.780// If cacheNTLMProp is false, and disableKeepAlive is false,781// then we need to examine the response headers to figure out782// whether we are doing NTLM authentication. If we do NTLM,783// and cacheNTLMProp is false, than we can't keep this connection784// alive: we will switch disableKeepAlive to true.785boolean canKeepAlive = !disableKeepAlive;786if (canKeepAlive && (cacheNTLMProp == false || cacheSPNEGOProp == false)787&& authenticate != null) {788authenticate = authenticate.toLowerCase(Locale.US);789if (cacheNTLMProp == false) {790canKeepAlive &= !authenticate.startsWith("ntlm ");791}792if (cacheSPNEGOProp == false) {793canKeepAlive &= !authenticate.startsWith("negotiate ");794canKeepAlive &= !authenticate.startsWith("kerberos ");795}796}797disableKeepAlive |= !canKeepAlive;798799if (keep != null && keep.toLowerCase(Locale.US).equals("keep-alive")) {800/* some servers, notably Apache1.1, send something like:801* "Keep-Alive: timeout=15, max=1" which we should respect.802*/803if (disableKeepAlive) {804keepAliveConnections = 1;805} else {806HeaderParser p = new HeaderParser(807responses.findValue("Keep-Alive"));808/* default should be larger in case of proxy */809keepAliveConnections = p.findInt("max", usingProxy?50:5);810keepAliveTimeout = p.findInt("timeout", usingProxy?60:5);811}812} else if (b[7] != '0') {813/*814* We're talking 1.1 or later. Keep persistent until815* the server says to close.816*/817if (keep != null || disableKeepAlive) {818/*819* The only Connection token we understand is close.820* Paranoia: if there is any Connection header then821* treat as non-persistent.822*/823keepAliveConnections = 1;824} else {825keepAliveConnections = 5;826}827}828} else if (nread != 8) {829if (!failedOnce && requests != null) {830failedOnce = true;831if (getRequestMethod().equals("CONNECT")832|| streaming833|| (httpuc.getRequestMethod().equals("POST")834&& !retryPostProp)) {835// do not retry the request836} else {837closeServer();838cachedHttpClient = false;839openServer();840if (needsTunneling()) {841MessageHeader origRequests = requests;842httpuc.doTunneling();843requests = origRequests;844}845afterConnect();846writeRequests(requests, poster);847return parseHTTP(responses, pi, httpuc);848}849}850throw new SocketException("Unexpected end of file from server");851} else {852// we can't vouche for what this is....853responses.set("Content-type", "unknown/unknown");854}855} catch (IOException e) {856throw e;857}858859int code = -1;860try {861String resp;862resp = responses.getValue(0);863/* should have no leading/trailing LWS864* expedite the typical case by assuming it has865* form "HTTP/1.x <WS> 2XX <mumble>"866*/867int ind;868ind = resp.indexOf(' ');869while(resp.charAt(ind) == ' ')870ind++;871code = Integer.parseInt(resp.substring(ind, ind + 3));872} catch (Exception e) {}873874if (code == HTTP_CONTINUE && ignoreContinue) {875responses.reset();876return parseHTTPHeader(responses, pi, httpuc);877}878879long cl = -1;880881/*882* Set things up to parse the entity body of the reply.883* We should be smarter about avoid pointless work when884* the HTTP method and response code indicate there will be885* no entity body to parse.886*/887String te = responses.findValue("Transfer-Encoding");888if (te != null && te.equalsIgnoreCase("chunked")) {889serverInput = new ChunkedInputStream(serverInput, this, responses);890891/*892* If keep alive not specified then close after the stream893* has completed.894*/895if (keepAliveConnections <= 1) {896keepAliveConnections = 1;897keepingAlive = false;898} else {899keepingAlive = !disableKeepAlive;900}901failedOnce = false;902} else {903904/*905* If it's a keep alive connection then we will keep906* (alive if :-907* 1. content-length is specified, or908* 2. "Not-Modified" or "No-Content" responses - RFC 2616 states that909* 204 or 304 response must not include a message body.910*/911String cls = responses.findValue("content-length");912if (cls != null) {913try {914cl = Long.parseLong(cls);915} catch (NumberFormatException e) {916cl = -1;917}918}919String requestLine = requests.getKey(0);920921if ((requestLine != null &&922(requestLine.startsWith("HEAD"))) ||923code == HttpURLConnection.HTTP_NOT_MODIFIED ||924code == HttpURLConnection.HTTP_NO_CONTENT) {925cl = 0;926}927928if (keepAliveConnections > 1 &&929(cl >= 0 ||930code == HttpURLConnection.HTTP_NOT_MODIFIED ||931code == HttpURLConnection.HTTP_NO_CONTENT)) {932keepingAlive = !disableKeepAlive;933failedOnce = false;934} else if (keepingAlive) {935/* Previously we were keeping alive, and now we're not. Remove936* this from the cache (but only here, once) - otherwise we get937* multiple removes and the cache count gets messed up.938*/939keepingAlive=false;940}941}942943/* wrap a KeepAliveStream/MeteredStream around it if appropriate */944945if (cl > 0) {946// In this case, content length is well known, so it is okay947// to wrap the input stream with KeepAliveStream/MeteredStream.948949if (pi != null) {950// Progress monitor is enabled951pi.setContentType(responses.findValue("content-type"));952}953954// If disableKeepAlive == true, the client will not be returned955// to the cache. But we still need to use a keepalive stream to956// allow the multi-message authentication exchange on the connection957boolean useKeepAliveStream = isKeepingAlive() || disableKeepAlive;958if (useKeepAliveStream) {959// Wrap KeepAliveStream if keep alive is enabled.960logFinest("KeepAlive stream used: " + url);961serverInput = new KeepAliveStream(serverInput, pi, cl, this);962failedOnce = false;963}964else {965serverInput = new MeteredStream(serverInput, pi, cl);966}967}968else if (cl == -1) {969// In this case, content length is unknown - the input970// stream would simply be a regular InputStream or971// ChunkedInputStream.972973if (pi != null) {974// Progress monitoring is enabled.975976pi.setContentType(responses.findValue("content-type"));977978// Wrap MeteredStream for tracking indeterministic979// progress, even if the input stream is ChunkedInputStream.980serverInput = new MeteredStream(serverInput, pi, cl);981}982else {983// Progress monitoring is disabled, and there is no984// need to wrap an unknown length input stream.985986// ** This is an no-op **987}988}989else {990if (pi != null)991pi.finishTracking();992}993994return ret;995}996997public synchronized InputStream getInputStream() {998return serverInput;999}10001001public OutputStream getOutputStream() {1002return serverOutput;1003}10041005@Override1006public String toString() {1007return getClass().getName()+"("+url+")";1008}10091010public final boolean isKeepingAlive() {1011return getHttpKeepAliveSet() && keepingAlive;1012}10131014public void setCacheRequest(CacheRequest cacheRequest) {1015this.cacheRequest = cacheRequest;1016}10171018CacheRequest getCacheRequest() {1019return cacheRequest;1020}10211022String getRequestMethod() {1023if (requests != null) {1024String requestLine = requests.getKey(0);1025if (requestLine != null) {1026return requestLine.split("\\s+")[0];1027}1028}1029return "";1030}10311032@Override1033protected void finalize() throws Throwable {1034// This should do nothing. The stream finalizer will1035// close the fd.1036}10371038public void setDoNotRetry(boolean value) {1039// failedOnce is used to determine if a request should be retried.1040failedOnce = value;1041}10421043public void setIgnoreContinue(boolean value) {1044ignoreContinue = value;1045}10461047/* Use only on connections in error. */1048@Override1049public void closeServer() {1050try {1051keepingAlive = false;1052serverSocket.close();1053} catch (Exception e) {}1054}10551056/**1057* @return the proxy host being used for this client, or null1058* if we're not going through a proxy1059*/1060public String getProxyHostUsed() {1061if (!usingProxy) {1062return null;1063} else {1064return ((InetSocketAddress)proxy.address()).getHostString();1065}1066}10671068/**1069* @return the proxy port being used for this client. Meaningless1070* if getProxyHostUsed() gives null.1071*/1072public int getProxyPortUsed() {1073if (usingProxy)1074return ((InetSocketAddress)proxy.address()).getPort();1075return -1;1076}1077}107810791080