HttpEngine.java revision faf49723fb689c626f69876e718c58018eff8ee7
1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/* 2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Licensed to the Apache Software Foundation (ASF) under one or more 3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * contributor license agreements. See the NOTICE file distributed with 4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * this work for additional information regarding copyright ownership. 5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * The ASF licenses this file to You under the Apache License, Version 2.0 6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * (the "License"); you may not use this file except in compliance with 7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * the License. You may obtain a copy of the License at 8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Unless required by applicable law or agreed to in writing, software 12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, 13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * See the License for the specific language governing permissions and 15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * limitations under the License. 16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 17c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 182231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpackage com.squareup.okhttp.internal.http; 19c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 202231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.Connection; 212231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.ConnectionPool; 22faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.OkAuthenticator; 237407d6984ce69693097befc9b72609a8156463bbNarayan Kamathimport com.squareup.okhttp.OkHttpClient; 24faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.Route; 25faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.internal.AbstractOutputStream; 26faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.internal.FaultRecoveringOutputStream; 272231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.Util; 28c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.FileNotFoundException; 29c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException; 30c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.InputStream; 31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.OutputStream; 322231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.CookieHandler; 33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.HttpRetryException; 342231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.HttpURLConnection; 35c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.InetSocketAddress; 36c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.ProtocolException; 37c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.Proxy; 382231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.ProxySelector; 39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.SocketPermission; 40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URL; 41c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.security.Permission; 422231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.security.cert.CertificateException; 43faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport java.util.ArrayList; 44c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.List; 45c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Map; 46faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport java.util.Set; 477407d6984ce69693097befc9b72609a8156463bbNarayan Kamathimport javax.net.ssl.HostnameVerifier; 482231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.SSLHandshakeException; 497407d6984ce69693097befc9b72609a8156463bbNarayan Kamathimport javax.net.ssl.SSLSocketFactory; 50c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 5154cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.getEffectivePort; 5254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 53c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/** 54c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * This implementation uses HttpEngine to send requests and receive responses. 55c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * This class may use multiple HttpEngines to follow redirects, authentication 56c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * retries, etc. to retrieve the final response body. 57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <h3>What does 'connected' mean?</h3> 59c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * This class inherits a {@code connected} field from the superclass. That field 60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * is <strong>not</strong> used to indicate not whether this URLConnection is 61c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * currently connected. Instead, it indicates whether a connection has ever been 62c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * attempted. Once a connection has been attempted, certain properties (request 63c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * header fields, request method, etc.) are immutable. Test the {@code 64c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * connection} field on this class for null/non-null to determine of an instance 65c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * is currently connected to a server. 66c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 672231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpublic class HttpURLConnectionImpl extends HttpURLConnection { 68faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 69faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath /** Numeric status code, 307: Temporary Redirect. */ 70faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath static final int HTTP_TEMP_REDIRECT = 307; 71faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 7254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 7354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * How many redirects should we follow? Chrome follows 21; Firefox, curl, 7454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5. 7554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 7654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private static final int MAX_REDIRECTS = 20; 7754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 78faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath /** 79faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * The minimum number of request body bytes to transmit before we're willing 80faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * to let a routine {@link IOException} bubble up to the user. This is used to 81faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * size a buffer for data that will be replayed upon error. 82faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath */ 83faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private static final int MAX_REPLAY_BUFFER_LENGTH = 8192; 84faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 857407d6984ce69693097befc9b72609a8156463bbNarayan Kamath private final boolean followProtocolRedirects; 867407d6984ce69693097befc9b72609a8156463bbNarayan Kamath 877407d6984ce69693097befc9b72609a8156463bbNarayan Kamath /** The proxy requested by the client, or null for a proxy to be selected automatically. */ 887407d6984ce69693097befc9b72609a8156463bbNarayan Kamath final Proxy requestedProxy; 8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 9054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson final ProxySelector proxySelector; 9154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson final CookieHandler cookieHandler; 92faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath final OkResponseCache responseCache; 9354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson final ConnectionPool connectionPool; 947407d6984ce69693097befc9b72609a8156463bbNarayan Kamath /* SSL configuration; necessary for HTTP requests that get redirected to HTTPS. */ 957407d6984ce69693097befc9b72609a8156463bbNarayan Kamath SSLSocketFactory sslSocketFactory; 967407d6984ce69693097befc9b72609a8156463bbNarayan Kamath HostnameVerifier hostnameVerifier; 97faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private List<String> transports; 98faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath OkAuthenticator authenticator; 99faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath final Set<Route> failedRoutes; 10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 10154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final RawHeaders rawRequestHeaders = new RawHeaders(); 10254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 10354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private int redirectionCount; 104faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private FaultRecoveringOutputStream faultRecoveringRequestBody; 10554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 10654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson protected IOException httpEngineFailure; 10754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson protected HttpEngine httpEngine; 10854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 109faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath public HttpURLConnectionImpl(URL url, OkHttpClient client, OkResponseCache responseCache, 110faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath Set<Route> failedRoutes) { 11154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson super(url); 1127407d6984ce69693097befc9b72609a8156463bbNarayan Kamath this.followProtocolRedirects = client.getFollowProtocolRedirects(); 113faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath this.failedRoutes = failedRoutes; 1147407d6984ce69693097befc9b72609a8156463bbNarayan Kamath this.requestedProxy = client.getProxy(); 1157407d6984ce69693097befc9b72609a8156463bbNarayan Kamath this.proxySelector = client.getProxySelector(); 1167407d6984ce69693097befc9b72609a8156463bbNarayan Kamath this.cookieHandler = client.getCookieHandler(); 1177407d6984ce69693097befc9b72609a8156463bbNarayan Kamath this.connectionPool = client.getConnectionPool(); 1187407d6984ce69693097befc9b72609a8156463bbNarayan Kamath this.sslSocketFactory = client.getSslSocketFactory(); 1197407d6984ce69693097befc9b72609a8156463bbNarayan Kamath this.hostnameVerifier = client.getHostnameVerifier(); 120faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath this.transports = client.getTransports(); 121faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath this.authenticator = client.getAuthenticator(); 122faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath this.responseCache = responseCache; 123faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 124faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 125faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath Set<Route> getFailedRoutes() { 126faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return failedRoutes; 127faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 128faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 129faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath List<String> getTransports() { 130faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return transports; 13154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 13254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 13354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final void connect() throws IOException { 13454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson initHttpEngine(); 13554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson boolean success; 13654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson do { 13754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson success = execute(false); 13854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } while (!success); 13954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 14154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final void disconnect() { 14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Calling disconnect() before a connection exists should have no effect. 14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (httpEngine != null) { 14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // We close the response body here instead of in 14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // HttpEngine.release because that is called when input 14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // has been completely read from the underlying socket. 14754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // However the response body can be a GZIPInputStream that 14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // still has unread data. 14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (httpEngine.hasResponse()) { 15054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Util.closeQuietly(httpEngine.getResponseBody()); 15154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson httpEngine.release(true); 15354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 15554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 15654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 15754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns an input stream from the server in the case of error such as the 15854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * requested file (txt, htm, html) is not found on the remote server. 15954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 16054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final InputStream getErrorStream() { 16154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson try { 16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson HttpEngine response = getResponse(); 16354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (response.hasResponseBody() && response.getResponseCode() >= HTTP_BAD_REQUEST) { 16454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return response.getResponseBody(); 16554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 16654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return null; 16754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } catch (IOException e) { 16854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return null; 16954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 17054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 17154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 17254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 17354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns the value of the field at {@code position}. Returns null if there 17454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * are fewer than {@code position} headers. 17554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final String getHeaderField(int position) { 17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson try { 17854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return getResponse().getResponseHeaders().getHeaders().getValue(position); 17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } catch (IOException e) { 18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return null; 18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 18254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 18454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 18554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns the value of the field corresponding to the {@code fieldName}, or 18654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * null if there is no such field. If the field has multiple values, the 18754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * last value is returned. 18854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 18954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final String getHeaderField(String fieldName) { 19054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson try { 19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson RawHeaders rawHeaders = getResponse().getResponseHeaders().getHeaders(); 19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return fieldName == null ? rawHeaders.getStatusLine() : rawHeaders.get(fieldName); 19354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } catch (IOException e) { 19454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return null; 19554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 19654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 19754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final String getHeaderFieldKey(int position) { 19954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson try { 20054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return getResponse().getResponseHeaders().getHeaders().getFieldName(position); 20154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } catch (IOException e) { 20254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return null; 20354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 20454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 20554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final Map<String, List<String>> getHeaderFields() { 20754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson try { 20854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return getResponse().getResponseHeaders().getHeaders().toMultimap(true); 20954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } catch (IOException e) { 21054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return null; 21154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 21254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 21354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 21454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final Map<String, List<String>> getRequestProperties() { 21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (connected) { 21654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException( 21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson "Cannot access request header fields after connection is set"); 21854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 21954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return rawRequestHeaders.toMultimap(false); 22054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 22154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 22254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final InputStream getInputStream() throws IOException { 22354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!doInput) { 22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new ProtocolException("This protocol does not support input"); 22554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 22654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 22754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson HttpEngine response = getResponse(); 22854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 22954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // if the requested file does not exist, throw an exception formerly the 23054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Error page from the server was returned if the requested file was 23154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // text/html this has changed to return FileNotFoundException for all 23254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // file types 23354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (getResponseCode() >= HTTP_BAD_REQUEST) { 23454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new FileNotFoundException(url.toString()); 23554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 23654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 23754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson InputStream result = response.getResponseBody(); 23854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (result == null) { 23954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new ProtocolException("No response body exists; responseCode=" + getResponseCode()); 24054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 24154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return result; 24254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 24354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 24454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final OutputStream getOutputStream() throws IOException { 24554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson connect(); 24654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 247faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath OutputStream out = httpEngine.getRequestBody(); 248faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (out == null) { 24954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new ProtocolException("method does not support a request body: " + method); 25054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else if (httpEngine.hasResponse()) { 25154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new ProtocolException("cannot write request body after response has been read"); 25254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 25354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 254faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (faultRecoveringRequestBody == null) { 255faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath faultRecoveringRequestBody = new FaultRecoveringOutputStream(MAX_REPLAY_BUFFER_LENGTH, out) { 256faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath @Override protected OutputStream replacementStream(IOException e) throws IOException { 257faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (httpEngine.getRequestBody() instanceof AbstractOutputStream 258faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath && ((AbstractOutputStream) httpEngine.getRequestBody()).isClosed()) { 259faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return null; // Don't recover once the underlying stream has been closed. 260faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 261faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (handleFailure(e)) { 262faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return httpEngine.getRequestBody(); 263faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 264faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return null; // This is a permanent failure. 265faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 266faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath }; 267faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 268faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 269faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return faultRecoveringRequestBody; 27054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 27154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 27254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final Permission getPermission() throws IOException { 2737407d6984ce69693097befc9b72609a8156463bbNarayan Kamath String hostName = getURL().getHost(); 2747407d6984ce69693097befc9b72609a8156463bbNarayan Kamath int hostPort = Util.getEffectivePort(getURL()); 2757407d6984ce69693097befc9b72609a8156463bbNarayan Kamath if (usingProxy()) { 2767407d6984ce69693097befc9b72609a8156463bbNarayan Kamath InetSocketAddress proxyAddress = (InetSocketAddress) requestedProxy.address(); 2777407d6984ce69693097befc9b72609a8156463bbNarayan Kamath hostName = proxyAddress.getHostName(); 2787407d6984ce69693097befc9b72609a8156463bbNarayan Kamath hostPort = proxyAddress.getPort(); 2797407d6984ce69693097befc9b72609a8156463bbNarayan Kamath } 2807407d6984ce69693097befc9b72609a8156463bbNarayan Kamath return new SocketPermission(hostName + ":" + hostPort, "connect, resolve"); 28154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 28254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 28354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final String getRequestProperty(String field) { 28454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (field == null) { 28554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return null; 28654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 28754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return rawRequestHeaders.get(field); 28854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 28954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 29054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void initHttpEngine() throws IOException { 29154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (httpEngineFailure != null) { 29254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw httpEngineFailure; 29354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else if (httpEngine != null) { 29454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 29554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 29654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 29754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson connected = true; 29854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson try { 29954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (doOutput) { 30054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (method.equals("GET")) { 30154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // they are requesting a stream to write to. This implies a POST method 30254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson method = "POST"; 30354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else if (!method.equals("POST") && !method.equals("PUT")) { 30454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // If the request method is neither POST nor PUT, then you're not writing 30554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new ProtocolException(method + " does not support writing"); 306c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 30754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 30854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson httpEngine = newHttpEngine(method, rawRequestHeaders, null, null); 30954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } catch (IOException e) { 31054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson httpEngineFailure = e; 31154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw e; 31254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 31354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 3157407d6984ce69693097befc9b72609a8156463bbNarayan Kamath protected HttpURLConnection getHttpConnectionToCache() { 3167407d6984ce69693097befc9b72609a8156463bbNarayan Kamath return this; 3177407d6984ce69693097befc9b72609a8156463bbNarayan Kamath } 3187407d6984ce69693097befc9b72609a8156463bbNarayan Kamath 3197407d6984ce69693097befc9b72609a8156463bbNarayan Kamath private HttpEngine newHttpEngine(String method, RawHeaders requestHeaders, 32054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Connection connection, RetryableOutputStream requestBody) throws IOException { 3217407d6984ce69693097befc9b72609a8156463bbNarayan Kamath if (url.getProtocol().equals("http")) { 3227407d6984ce69693097befc9b72609a8156463bbNarayan Kamath return new HttpEngine(this, method, requestHeaders, connection, requestBody); 3237407d6984ce69693097befc9b72609a8156463bbNarayan Kamath } else if (url.getProtocol().equals("https")) { 3247407d6984ce69693097befc9b72609a8156463bbNarayan Kamath return new HttpsURLConnectionImpl.HttpsEngine( 3257407d6984ce69693097befc9b72609a8156463bbNarayan Kamath this, method, requestHeaders, connection, requestBody); 3267407d6984ce69693097befc9b72609a8156463bbNarayan Kamath } else { 3277407d6984ce69693097befc9b72609a8156463bbNarayan Kamath throw new AssertionError(); 3287407d6984ce69693097befc9b72609a8156463bbNarayan Kamath } 32954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 33054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 33154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 33254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Aggressively tries to get the final HTTP response, potentially making 33354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * many HTTP requests in the process in order to cope with redirects and 33454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * authentication. 33554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 33654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private HttpEngine getResponse() throws IOException { 33754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson initHttpEngine(); 33854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (httpEngine.hasResponse()) { 34054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return httpEngine; 34154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 34254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 34354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson while (true) { 34454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!execute(true)) { 34554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson continue; 34654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 34754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 34854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Retry retry = processResponseHeaders(); 34954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (retry == Retry.NONE) { 35054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson httpEngine.automaticallyReleaseConnectionToPool(); 35154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return httpEngine; 35254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 35354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 35454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // The first request was insufficient. Prepare for another... 35554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson String retryMethod = method; 35654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson OutputStream requestBody = httpEngine.getRequestBody(); 35754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 35854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Although RFC 2616 10.3.2 specifies that a HTTP_MOVED_PERM 35954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // redirect should keep the same method, Chrome, Firefox and the 36054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // RI all issue GETs when following any redirect. 36154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson int responseCode = getResponseCode(); 36254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseCode == HTTP_MULT_CHOICE 36354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson || responseCode == HTTP_MOVED_PERM 36454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson || responseCode == HTTP_MOVED_TEMP 36554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson || responseCode == HTTP_SEE_OTHER) { 36654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson retryMethod = "GET"; 36754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestBody = null; 36854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 36954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 37054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (requestBody != null && !(requestBody instanceof RetryableOutputStream)) { 37154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new HttpRetryException("Cannot retry streamed HTTP body", 37254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson httpEngine.getResponseCode()); 37354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 37454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 37554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (retry == Retry.DIFFERENT_CONNECTION) { 37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson httpEngine.automaticallyReleaseConnectionToPool(); 37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 37954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson httpEngine.release(false); 38054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 38154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson httpEngine = newHttpEngine(retryMethod, rawRequestHeaders, httpEngine.getConnection(), 38254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson (RetryableOutputStream) requestBody); 38354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 38454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 38554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 38654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 38754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Sends a request and optionally reads a response. Returns true if the 38854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * request was successfully executed, and false if the request can be 38954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * retried. Throws an exception if the request failed permanently. 39054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 39154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private boolean execute(boolean readResponse) throws IOException { 39254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson try { 39354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson httpEngine.sendRequest(); 39454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (readResponse) { 39554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson httpEngine.readResponse(); 39654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 39754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return true; 39854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } catch (IOException e) { 399faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (handleFailure(e)) { 40054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return false; 401faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { 402faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw e; 40354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 404faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 405faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 406faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 407faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath /** 408faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * Report and attempt to recover from {@code e}. Returns true if the HTTP 409faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * engine was replaced and the request should be retried. Otherwise the 410faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * failure is permanent. 411faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath */ 412faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private boolean handleFailure(IOException e) throws IOException { 413faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath RouteSelector routeSelector = httpEngine.routeSelector; 414faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (routeSelector != null && httpEngine.connection != null) { 415faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath routeSelector.connectFailed(httpEngine.connection, e); 416faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 417faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 418faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath OutputStream requestBody = httpEngine.getRequestBody(); 419faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath boolean canRetryRequestBody = requestBody == null 420faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath || requestBody instanceof RetryableOutputStream 421faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath || (faultRecoveringRequestBody != null && faultRecoveringRequestBody.isRecoverable()); 422faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (routeSelector == null && httpEngine.connection == null // No connection. 423faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt. 424faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath || !isRecoverable(e) 425faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath || !canRetryRequestBody) { 42654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson httpEngineFailure = e; 427faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return false; 42854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 429faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 430faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath httpEngine.release(true); 431faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath RetryableOutputStream retryableOutputStream = requestBody instanceof RetryableOutputStream 432faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath ? (RetryableOutputStream) requestBody 433faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath : null; 434faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath httpEngine = newHttpEngine(method, rawRequestHeaders, null, retryableOutputStream); 435faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath httpEngine.routeSelector = routeSelector; // Keep the same routeSelector. 436faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (faultRecoveringRequestBody != null && faultRecoveringRequestBody.isRecoverable()) { 437faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath httpEngine.sendRequest(); 438faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath faultRecoveringRequestBody.replaceStream(httpEngine.getRequestBody()); 439faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 440faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return true; 44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 44254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 44354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private boolean isRecoverable(IOException e) { 44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // If the problem was a CertificateException from the X509TrustManager, 44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // do not retry, we didn't have an abrupt server initiated exception. 44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson boolean sslFailure = 44754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson e instanceof SSLHandshakeException && e.getCause() instanceof CertificateException; 44854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson boolean protocolFailure = e instanceof ProtocolException; 44954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return !sslFailure && !protocolFailure; 45054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 45154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 452faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath public HttpEngine getHttpEngine() { 45354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return httpEngine; 45454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 45654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson enum Retry { 45754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson NONE, 45854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson SAME_CONNECTION, 45954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson DIFFERENT_CONNECTION 46054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 46154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 46254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 46354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns the retry action to take for the current response headers. The 46454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * headers, proxy and target URL or this connection may be adjusted to 46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * prepare for a follow up request. 46654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 46754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private Retry processResponseHeaders() throws IOException { 4687407d6984ce69693097befc9b72609a8156463bbNarayan Kamath Proxy selectedProxy = httpEngine.connection != null 469faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath ? httpEngine.connection.getRoute().getProxy() 4707407d6984ce69693097befc9b72609a8156463bbNarayan Kamath : requestedProxy; 471faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath final int responseCode = getResponseCode(); 472faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath switch (responseCode) { 47354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson case HTTP_PROXY_AUTH: 4747407d6984ce69693097befc9b72609a8156463bbNarayan Kamath if (selectedProxy.type() != Proxy.Type.HTTP) { 47554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy"); 476c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // fall-through 47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson case HTTP_UNAUTHORIZED: 479faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath boolean credentialsFound = HttpAuthenticator.processAuthHeader(authenticator, 480faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath getResponseCode(), httpEngine.getResponseHeaders().getHeaders(), rawRequestHeaders, 481faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath selectedProxy, url); 48254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return credentialsFound ? Retry.SAME_CONNECTION : Retry.NONE; 48354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 48454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson case HTTP_MULT_CHOICE: 48554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson case HTTP_MOVED_PERM: 48654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson case HTTP_MOVED_TEMP: 48754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson case HTTP_SEE_OTHER: 488faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case HTTP_TEMP_REDIRECT: 48954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!getInstanceFollowRedirects()) { 49054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return Retry.NONE; 491c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (++redirectionCount > MAX_REDIRECTS) { 49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new ProtocolException("Too many redirects: " + redirectionCount); 494c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 495faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (responseCode == HTTP_TEMP_REDIRECT && !method.equals("GET") && !method.equals("HEAD")) { 496faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // "If the 307 status code is received in response to a request other than GET or HEAD, 497faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // the user agent MUST NOT automatically redirect the request" 498faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return Retry.NONE; 499faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 50054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson String location = getHeaderField("Location"); 50154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (location == null) { 50254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return Retry.NONE; 503c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson URL previousUrl = url; 50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson url = new URL(previousUrl, location); 5067407d6984ce69693097befc9b72609a8156463bbNarayan Kamath if (!url.getProtocol().equals("https") && !url.getProtocol().equals("http")) { 5077407d6984ce69693097befc9b72609a8156463bbNarayan Kamath return Retry.NONE; // Don't follow redirects to unsupported protocols. 5087407d6984ce69693097befc9b72609a8156463bbNarayan Kamath } 5097407d6984ce69693097befc9b72609a8156463bbNarayan Kamath boolean sameProtocol = previousUrl.getProtocol().equals(url.getProtocol()); 5107407d6984ce69693097befc9b72609a8156463bbNarayan Kamath if (!sameProtocol && !followProtocolRedirects) { 5117407d6984ce69693097befc9b72609a8156463bbNarayan Kamath return Retry.NONE; // This client doesn't follow redirects across protocols. 512c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 5137407d6984ce69693097befc9b72609a8156463bbNarayan Kamath boolean sameHost = previousUrl.getHost().equals(url.getHost()); 5147407d6984ce69693097befc9b72609a8156463bbNarayan Kamath boolean samePort = getEffectivePort(previousUrl) == getEffectivePort(url); 5157407d6984ce69693097befc9b72609a8156463bbNarayan Kamath if (sameHost && samePort && sameProtocol) { 51654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return Retry.SAME_CONNECTION; 51754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 51854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return Retry.DIFFERENT_CONNECTION; 5192231db3e6bb54447a9b14cf004a6cb03c373651cjwilson } 5202231db3e6bb54447a9b14cf004a6cb03c373651cjwilson 52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson default: 52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return Retry.NONE; 5232231db3e6bb54447a9b14cf004a6cb03c373651cjwilson } 52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 5252231db3e6bb54447a9b14cf004a6cb03c373651cjwilson 52654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** @see java.net.HttpURLConnection#setFixedLengthStreamingMode(int) */ 52754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson final int getFixedContentLength() { 52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return fixedContentLength; 52954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 530c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 53154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** @see java.net.HttpURLConnection#setChunkedStreamingMode(int) */ 53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson final int getChunkLength() { 53354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return chunkLength; 53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 535c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final boolean usingProxy() { 5377407d6984ce69693097befc9b72609a8156463bbNarayan Kamath return (requestedProxy != null && requestedProxy.type() != Proxy.Type.DIRECT); 53854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 539c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public String getResponseMessage() throws IOException { 54154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return getResponse().getResponseHeaders().getHeaders().getResponseMessage(); 54254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 543c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final int getResponseCode() throws IOException { 54554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return getResponse().getResponseCode(); 54654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 547c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 54854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final void setRequestProperty(String field, String newValue) { 54954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (connected) { 55054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException("Cannot set request property after connection is made"); 551c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 55254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (field == null) { 55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new NullPointerException("field == null"); 554c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 555faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if ("X-Android-Transports".equals(field)) { 556faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath setTransports(newValue, false /* append */); 557faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { 558faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath rawRequestHeaders.set(field, newValue); 559faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 56054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 561c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 56254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public final void addRequestProperty(String field, String value) { 56354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (connected) { 56454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException("Cannot add request property after connection is made"); 565c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 56654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (field == null) { 56754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new NullPointerException("field == null"); 568c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 569faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 570faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if ("X-Android-Transports".equals(field)) { 571faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath setTransports(value, true /* append */); 572faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { 573faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath rawRequestHeaders.add(field, value); 574faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 575faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 576faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 577faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath /* 578faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * Splits and validates a comma-separated string of transports. 579faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * When append == false, we require that the transport list contains "http/1.1". 580faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath */ 581faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private void setTransports(String transportsString, boolean append) { 582faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (transportsString == null) { 583faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new NullPointerException("transportsString == null"); 584faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 585faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 586faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath String[] transports = transportsString.split(",", -1); 587faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath ArrayList<String> transportsList = new ArrayList<String>(); 588faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (!append) { 589faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // If we're not appending to the list, we need to make sure 590faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // the list contains "http/1.1". We do this in a separate loop 591faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // to avoid modifying any state before we validate the input. 592faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath boolean containsHttp = false; 593faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (int i = 0; i < transports.length; ++i) { 594faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if ("http/1.1".equals(transports[i])) { 595faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath containsHttp = true; 596faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath break; 597faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 598faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 599faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 600faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (!containsHttp) { 601faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalArgumentException("Transport list doesn't contain http/1.1"); 602faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 603faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { 604faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath transportsList.addAll(this.transports); 605faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 606faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 607faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (int i = 0; i < transports.length; ++i) { 608faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (transports[i].length() == 0) { 609faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalArgumentException("Transport list contains an empty transport"); 610faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 611faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 612faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (!transportsList.contains(transports[i])) { 613faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath transportsList.add(transports[i]); 614faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 615faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 616faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 617faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath this.transports = Util.immutableList(transportsList); 61854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 619c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath} 620