Home | History | Annotate | Download | only in http
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 package com.squareup.okhttp.internal.http;
     18 
     19 import com.squareup.okhttp.Connection;
     20 import com.squareup.okhttp.OkHttpClient;
     21 import com.squareup.okhttp.TunnelRequest;
     22 import java.io.IOException;
     23 import java.io.InputStream;
     24 import java.io.OutputStream;
     25 import java.net.CacheResponse;
     26 import java.net.HttpURLConnection;
     27 import java.net.ProtocolException;
     28 import java.net.SecureCacheResponse;
     29 import java.net.URL;
     30 import java.security.Permission;
     31 import java.security.Principal;
     32 import java.security.cert.Certificate;
     33 import java.util.List;
     34 import java.util.Map;
     35 import javax.net.ssl.HostnameVerifier;
     36 import javax.net.ssl.HttpsURLConnection;
     37 import javax.net.ssl.SSLPeerUnverifiedException;
     38 import javax.net.ssl.SSLSocket;
     39 import javax.net.ssl.SSLSocketFactory;
     40 
     41 import static com.squareup.okhttp.internal.Util.getEffectivePort;
     42 
     43 public final class HttpsURLConnectionImpl extends HttpsURLConnection {
     44 
     45   /** HttpUrlConnectionDelegate allows reuse of HttpURLConnectionImpl. */
     46   private final HttpUrlConnectionDelegate delegate;
     47 
     48   public HttpsURLConnectionImpl(URL url, OkHttpClient client) {
     49     super(url);
     50     delegate = new HttpUrlConnectionDelegate(url, client);
     51   }
     52 
     53   @Override public String getCipherSuite() {
     54     SecureCacheResponse cacheResponse = delegate.getSecureCacheResponse();
     55     if (cacheResponse != null) {
     56       return cacheResponse.getCipherSuite();
     57     }
     58     SSLSocket sslSocket = getSslSocket();
     59     if (sslSocket != null) {
     60       return sslSocket.getSession().getCipherSuite();
     61     }
     62     return null;
     63   }
     64 
     65   @Override public Certificate[] getLocalCertificates() {
     66     SecureCacheResponse cacheResponse = delegate.getSecureCacheResponse();
     67     if (cacheResponse != null) {
     68       List<Certificate> result = cacheResponse.getLocalCertificateChain();
     69       return result != null ? result.toArray(new Certificate[result.size()]) : null;
     70     }
     71     SSLSocket sslSocket = getSslSocket();
     72     if (sslSocket != null) {
     73       return sslSocket.getSession().getLocalCertificates();
     74     }
     75     return null;
     76   }
     77 
     78   @Override public Certificate[] getServerCertificates() throws SSLPeerUnverifiedException {
     79     SecureCacheResponse cacheResponse = delegate.getSecureCacheResponse();
     80     if (cacheResponse != null) {
     81       List<Certificate> result = cacheResponse.getServerCertificateChain();
     82       return result != null ? result.toArray(new Certificate[result.size()]) : null;
     83     }
     84     SSLSocket sslSocket = getSslSocket();
     85     if (sslSocket != null) {
     86       return sslSocket.getSession().getPeerCertificates();
     87     }
     88     return null;
     89   }
     90 
     91   @Override public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
     92     SecureCacheResponse cacheResponse = delegate.getSecureCacheResponse();
     93     if (cacheResponse != null) {
     94       return cacheResponse.getPeerPrincipal();
     95     }
     96     SSLSocket sslSocket = getSslSocket();
     97     if (sslSocket != null) {
     98       return sslSocket.getSession().getPeerPrincipal();
     99     }
    100     return null;
    101   }
    102 
    103   @Override public Principal getLocalPrincipal() {
    104     SecureCacheResponse cacheResponse = delegate.getSecureCacheResponse();
    105     if (cacheResponse != null) {
    106       return cacheResponse.getLocalPrincipal();
    107     }
    108     SSLSocket sslSocket = getSslSocket();
    109     if (sslSocket != null) {
    110       return sslSocket.getSession().getLocalPrincipal();
    111     }
    112     return null;
    113   }
    114 
    115   HttpEngine getHttpEngine() {
    116     return delegate.getHttpEngine();
    117   }
    118 
    119   private SSLSocket getSslSocket() {
    120     if (delegate.httpEngine == null || delegate.httpEngine.sentRequestMillis == -1) {
    121       throw new IllegalStateException("Connection has not yet been established");
    122     }
    123     return delegate.httpEngine instanceof HttpsEngine
    124         ? ((HttpsEngine) delegate.httpEngine).sslSocket
    125         : null; // Not HTTPS! Probably an https:// to http:// redirect.
    126   }
    127 
    128   @Override
    129   public void disconnect() {
    130     delegate.disconnect();
    131   }
    132 
    133   @Override
    134   public InputStream getErrorStream() {
    135     return delegate.getErrorStream();
    136   }
    137 
    138   @Override
    139   public String getRequestMethod() {
    140     return delegate.getRequestMethod();
    141   }
    142 
    143   @Override
    144   public int getResponseCode() throws IOException {
    145     return delegate.getResponseCode();
    146   }
    147 
    148   @Override
    149   public String getResponseMessage() throws IOException {
    150     return delegate.getResponseMessage();
    151   }
    152 
    153   @Override
    154   public void setRequestMethod(String method) throws ProtocolException {
    155     delegate.setRequestMethod(method);
    156   }
    157 
    158   @Override
    159   public boolean usingProxy() {
    160     return delegate.usingProxy();
    161   }
    162 
    163   @Override
    164   public boolean getInstanceFollowRedirects() {
    165     return delegate.getInstanceFollowRedirects();
    166   }
    167 
    168   @Override
    169   public void setInstanceFollowRedirects(boolean followRedirects) {
    170     delegate.setInstanceFollowRedirects(followRedirects);
    171   }
    172 
    173   @Override
    174   public void connect() throws IOException {
    175     connected = true;
    176     delegate.connect();
    177   }
    178 
    179   @Override
    180   public boolean getAllowUserInteraction() {
    181     return delegate.getAllowUserInteraction();
    182   }
    183 
    184   @Override
    185   public Object getContent() throws IOException {
    186     return delegate.getContent();
    187   }
    188 
    189   @SuppressWarnings("unchecked") // Spec does not generify
    190   @Override
    191   public Object getContent(Class[] types) throws IOException {
    192     return delegate.getContent(types);
    193   }
    194 
    195   @Override
    196   public String getContentEncoding() {
    197     return delegate.getContentEncoding();
    198   }
    199 
    200   @Override
    201   public int getContentLength() {
    202     return delegate.getContentLength();
    203   }
    204 
    205   @Override
    206   public String getContentType() {
    207     return delegate.getContentType();
    208   }
    209 
    210   @Override
    211   public long getDate() {
    212     return delegate.getDate();
    213   }
    214 
    215   @Override
    216   public boolean getDefaultUseCaches() {
    217     return delegate.getDefaultUseCaches();
    218   }
    219 
    220   @Override
    221   public boolean getDoInput() {
    222     return delegate.getDoInput();
    223   }
    224 
    225   @Override
    226   public boolean getDoOutput() {
    227     return delegate.getDoOutput();
    228   }
    229 
    230   @Override
    231   public long getExpiration() {
    232     return delegate.getExpiration();
    233   }
    234 
    235   @Override
    236   public String getHeaderField(int pos) {
    237     return delegate.getHeaderField(pos);
    238   }
    239 
    240   @Override
    241   public Map<String, List<String>> getHeaderFields() {
    242     return delegate.getHeaderFields();
    243   }
    244 
    245   @Override
    246   public Map<String, List<String>> getRequestProperties() {
    247     return delegate.getRequestProperties();
    248   }
    249 
    250   @Override
    251   public void addRequestProperty(String field, String newValue) {
    252     delegate.addRequestProperty(field, newValue);
    253   }
    254 
    255   @Override
    256   public String getHeaderField(String key) {
    257     return delegate.getHeaderField(key);
    258   }
    259 
    260   @Override
    261   public long getHeaderFieldDate(String field, long defaultValue) {
    262     return delegate.getHeaderFieldDate(field, defaultValue);
    263   }
    264 
    265   @Override
    266   public int getHeaderFieldInt(String field, int defaultValue) {
    267     return delegate.getHeaderFieldInt(field, defaultValue);
    268   }
    269 
    270   @Override
    271   public String getHeaderFieldKey(int position) {
    272     return delegate.getHeaderFieldKey(position);
    273   }
    274 
    275   @Override
    276   public long getIfModifiedSince() {
    277     return delegate.getIfModifiedSince();
    278   }
    279 
    280   @Override
    281   public InputStream getInputStream() throws IOException {
    282     return delegate.getInputStream();
    283   }
    284 
    285   @Override
    286   public long getLastModified() {
    287     return delegate.getLastModified();
    288   }
    289 
    290   @Override
    291   public OutputStream getOutputStream() throws IOException {
    292     return delegate.getOutputStream();
    293   }
    294 
    295   @Override
    296   public Permission getPermission() throws IOException {
    297     return delegate.getPermission();
    298   }
    299 
    300   @Override
    301   public String getRequestProperty(String field) {
    302     return delegate.getRequestProperty(field);
    303   }
    304 
    305   @Override
    306   public URL getURL() {
    307     return delegate.getURL();
    308   }
    309 
    310   @Override
    311   public boolean getUseCaches() {
    312     return delegate.getUseCaches();
    313   }
    314 
    315   @Override
    316   public void setAllowUserInteraction(boolean newValue) {
    317     delegate.setAllowUserInteraction(newValue);
    318   }
    319 
    320   @Override
    321   public void setDefaultUseCaches(boolean newValue) {
    322     delegate.setDefaultUseCaches(newValue);
    323   }
    324 
    325   @Override
    326   public void setDoInput(boolean newValue) {
    327     delegate.setDoInput(newValue);
    328   }
    329 
    330   @Override
    331   public void setDoOutput(boolean newValue) {
    332     delegate.setDoOutput(newValue);
    333   }
    334 
    335   @Override
    336   public void setIfModifiedSince(long newValue) {
    337     delegate.setIfModifiedSince(newValue);
    338   }
    339 
    340   @Override
    341   public void setRequestProperty(String field, String newValue) {
    342     delegate.setRequestProperty(field, newValue);
    343   }
    344 
    345   @Override
    346   public void setUseCaches(boolean newValue) {
    347     delegate.setUseCaches(newValue);
    348   }
    349 
    350   @Override
    351   public void setConnectTimeout(int timeoutMillis) {
    352     delegate.setConnectTimeout(timeoutMillis);
    353   }
    354 
    355   @Override
    356   public int getConnectTimeout() {
    357     return delegate.getConnectTimeout();
    358   }
    359 
    360   @Override
    361   public void setReadTimeout(int timeoutMillis) {
    362     delegate.setReadTimeout(timeoutMillis);
    363   }
    364 
    365   @Override
    366   public int getReadTimeout() {
    367     return delegate.getReadTimeout();
    368   }
    369 
    370   @Override
    371   public String toString() {
    372     return delegate.toString();
    373   }
    374 
    375   @Override
    376   public void setFixedLengthStreamingMode(int contentLength) {
    377     delegate.setFixedLengthStreamingMode(contentLength);
    378   }
    379 
    380   @Override
    381   public void setChunkedStreamingMode(int chunkLength) {
    382     delegate.setChunkedStreamingMode(chunkLength);
    383   }
    384 
    385   @Override public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
    386     delegate.hostnameVerifier = hostnameVerifier;
    387   }
    388 
    389   @Override public HostnameVerifier getHostnameVerifier() {
    390     return delegate.hostnameVerifier;
    391   }
    392 
    393   @Override public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
    394     delegate.sslSocketFactory = sslSocketFactory;
    395   }
    396 
    397   @Override public SSLSocketFactory getSSLSocketFactory() {
    398     return delegate.sslSocketFactory;
    399   }
    400 
    401   private final class HttpUrlConnectionDelegate extends HttpURLConnectionImpl {
    402     private HttpUrlConnectionDelegate(URL url, OkHttpClient client) {
    403       super(url, client);
    404     }
    405 
    406     @Override protected HttpURLConnection getHttpConnectionToCache() {
    407       return HttpsURLConnectionImpl.this;
    408     }
    409 
    410     public SecureCacheResponse getSecureCacheResponse() {
    411       return httpEngine instanceof HttpsEngine
    412           ? (SecureCacheResponse) httpEngine.getCacheResponse()
    413           : null;
    414     }
    415   }
    416 
    417   public static final class HttpsEngine extends HttpEngine {
    418     /**
    419      * Stash of HttpsEngine.connection.socket to implement requests like
    420      * {@link #getCipherSuite} even after the connection has been recycled.
    421      */
    422     private SSLSocket sslSocket;
    423 
    424     /**
    425      * @param policy the HttpURLConnectionImpl with connection configuration
    426      */
    427     public HttpsEngine(HttpURLConnectionImpl policy, String method, RawHeaders requestHeaders,
    428         Connection connection, RetryableOutputStream requestBody)
    429         throws IOException {
    430       super(policy, method, requestHeaders, connection, requestBody);
    431       this.sslSocket = connection != null ? (SSLSocket) connection.getSocket() : null;
    432     }
    433 
    434     @Override protected void connected(Connection connection) {
    435       this.sslSocket = (SSLSocket) connection.getSocket();
    436     }
    437 
    438     @Override protected boolean acceptCacheResponseType(CacheResponse cacheResponse) {
    439       return cacheResponse instanceof SecureCacheResponse;
    440     }
    441 
    442     @Override protected boolean includeAuthorityInRequestLine() {
    443       // Even if there is a proxy, it isn't involved. Always request just the file.
    444       return false;
    445     }
    446 
    447     @Override protected TunnelRequest getTunnelConfig() {
    448       String userAgent = requestHeaders.getUserAgent();
    449       if (userAgent == null) {
    450         userAgent = getDefaultUserAgent();
    451       }
    452 
    453       URL url = policy.getURL();
    454       return new TunnelRequest(url.getHost(), getEffectivePort(url), userAgent,
    455           requestHeaders.getProxyAuthorization());
    456     }
    457   }
    458 }
    459