All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squareup.okhttp.Connection Maven / Gradle / Ivy

There is a newer version: 2.7.5
Show newest version
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.squareup.okhttp;

import com.squareup.okhttp.internal.http.HttpConnection;
import com.squareup.okhttp.internal.http.HttpEngine;
import com.squareup.okhttp.internal.http.HttpTransport;
import com.squareup.okhttp.internal.http.RouteException;
import com.squareup.okhttp.internal.http.SocketConnector;
import com.squareup.okhttp.internal.http.SpdyTransport;
import com.squareup.okhttp.internal.http.Transport;
import com.squareup.okhttp.internal.spdy.SpdyConnection;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownServiceException;
import java.util.List;
import okio.BufferedSink;
import okio.BufferedSource;

/**
 * The sockets and streams of an HTTP, HTTPS, or HTTPS+SPDY connection. May be
 * used for multiple HTTP request/response exchanges. Connections may be direct
 * to the origin server or via a proxy.
 *
 * 

Typically instances of this class are created, connected and exercised * automatically by the HTTP client. Applications may use this class to monitor * HTTP connections as members of a {@linkplain ConnectionPool connection pool}. * *

Do not confuse this class with the misnamed {@code HttpURLConnection}, * which isn't so much a connection as a single request/response exchange. * *

Modern TLS

* There are tradeoffs when selecting which options to include when negotiating * a secure connection to a remote host. Newer TLS options are quite useful: *
    *
  • Server Name Indication (SNI) enables one IP address to negotiate secure * connections for multiple domain names. *
  • Application Layer Protocol Negotiation (ALPN) enables the HTTPS port * (443) to be used for different HTTP and SPDY protocols. *
* Unfortunately, older HTTPS servers refuse to connect when such options are * presented. Rather than avoiding these options entirely, this class allows a * connection to be attempted with modern options and then retried without them * should the attempt fail. */ public final class Connection { private final ConnectionPool pool; private final Route route; private Socket socket; private boolean connected = false; private HttpConnection httpConnection; private SpdyConnection spdyConnection; private Protocol protocol = Protocol.HTTP_1_1; private long idleStartTimeNs; private Handshake handshake; private int recycleCount; /** * The object that owns this connection. Null if it is shared (for SPDY), * belongs to a pool, or has been discarded. Guarded by {@code pool}, which * clears the owner when an incoming connection is recycled. */ private Object owner; public Connection(ConnectionPool pool, Route route) { this.pool = pool; this.route = route; } Object getOwner() { synchronized (pool) { return owner; } } void setOwner(Object owner) { if (isSpdy()) return; // SPDY connections are shared. synchronized (pool) { if (this.owner != null) throw new IllegalStateException("Connection already has an owner!"); this.owner = owner; } } /** * Attempts to clears the owner of this connection. Returns true if the owner * was cleared and the connection can be pooled or reused. This will return * false if the connection cannot be pooled or reused, such as if it was * closed with {@link #closeIfOwnedBy}. */ boolean clearOwner() { synchronized (pool) { if (owner == null) { // No owner? Don't reuse this connection. return false; } owner = null; return true; } } /** * Closes this connection if it is currently owned by {@code owner}. This also * strips the ownership of the connection so it cannot be pooled or reused. */ void closeIfOwnedBy(Object owner) throws IOException { if (isSpdy()) throw new IllegalStateException(); synchronized (pool) { if (this.owner != owner) { return; // Wrong owner. Perhaps a late disconnect? } this.owner = null; // Drop the owner so the connection won't be reused. } // Don't close() inside the synchronized block. socket.close(); } void connect(int connectTimeout, int readTimeout, int writeTimeout, Request request, List connectionSpecs, boolean connectionRetryEnabled) throws RouteException { if (connected) throw new IllegalStateException("already connected"); SocketConnector socketConnector = new SocketConnector(this, pool); SocketConnector.ConnectedSocket connectedSocket; if (route.address.getSslSocketFactory() != null) { // https:// communication connectedSocket = socketConnector.connectTls(connectTimeout, readTimeout, writeTimeout, request, route, connectionSpecs, connectionRetryEnabled); } else { // http:// communication. if (!connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) { throw new RouteException( new UnknownServiceException( "CLEARTEXT communication not supported: " + connectionSpecs)); } connectedSocket = socketConnector.connectCleartext(connectTimeout, readTimeout, route); } socket = connectedSocket.socket; handshake = connectedSocket.handshake; protocol = connectedSocket.alpnProtocol == null ? Protocol.HTTP_1_1 : connectedSocket.alpnProtocol; try { if (protocol == Protocol.SPDY_3 || protocol == Protocol.HTTP_2) { socket.setSoTimeout(0); // SPDY timeouts are set per-stream. spdyConnection = new SpdyConnection.Builder(route.address.uriHost, true, socket) .protocol(protocol).build(); spdyConnection.sendConnectionPreface(); } else { httpConnection = new HttpConnection(pool, this, socket); } } catch (IOException e) { throw new RouteException(e); } connected = true; } /** * Connects this connection if it isn't already. This creates tunnels, shares * the connection with the connection pool, and configures timeouts. */ void connectAndSetOwner(OkHttpClient client, Object owner, Request request) throws RouteException { setOwner(owner); if (!isConnected()) { List connectionSpecs = route.address.getConnectionSpecs(); connect(client.getConnectTimeout(), client.getReadTimeout(), client.getWriteTimeout(), request, connectionSpecs, client.getRetryOnConnectionFailure()); if (isSpdy()) { client.getConnectionPool().share(this); } client.routeDatabase().connected(getRoute()); } setTimeouts(client.getReadTimeout(), client.getWriteTimeout()); } /** Returns true if {@link #connect} has been attempted on this connection. */ boolean isConnected() { return connected; } /** Returns the route used by this connection. */ public Route getRoute() { return route; } /** * Returns the socket that this connection uses, or null if the connection * is not currently connected. */ public Socket getSocket() { return socket; } BufferedSource rawSource() { if (httpConnection == null) throw new UnsupportedOperationException(); return httpConnection.rawSource(); } BufferedSink rawSink() { if (httpConnection == null) throw new UnsupportedOperationException(); return httpConnection.rawSink(); } /** Returns true if this connection is alive. */ boolean isAlive() { return !socket.isClosed() && !socket.isInputShutdown() && !socket.isOutputShutdown(); } /** * Returns true if we are confident that we can read data from this * connection. This is more expensive and more accurate than {@link * #isAlive()}; callers should check {@link #isAlive()} first. */ boolean isReadable() { if (httpConnection != null) return httpConnection.isReadable(); return true; // SPDY connections, and connections before connect() are both optimistic. } void resetIdleStartTime() { if (spdyConnection != null) throw new IllegalStateException("spdyConnection != null"); this.idleStartTimeNs = System.nanoTime(); } /** Returns true if this connection is idle. */ boolean isIdle() { return spdyConnection == null || spdyConnection.isIdle(); } /** * Returns the time in ns when this connection became idle. Undefined if * this connection is not idle. */ long getIdleStartTimeNs() { return spdyConnection == null ? idleStartTimeNs : spdyConnection.getIdleStartTimeNs(); } public Handshake getHandshake() { return handshake; } /** Returns the transport appropriate for this connection. */ Transport newTransport(HttpEngine httpEngine) throws IOException { return (spdyConnection != null) ? new SpdyTransport(httpEngine, spdyConnection) : new HttpTransport(httpEngine, httpConnection); } /** * Returns true if this is a SPDY connection. Such connections can be used * in multiple HTTP requests simultaneously. */ boolean isSpdy() { return spdyConnection != null; } /** * Returns the protocol negotiated by this connection, or {@link * Protocol#HTTP_1_1} if no protocol has been negotiated. */ public Protocol getProtocol() { return protocol; } /** * Sets the protocol negotiated by this connection. Typically this is used * when an HTTP/1.1 request is sent and an HTTP/1.0 response is received. */ void setProtocol(Protocol protocol) { if (protocol == null) throw new IllegalArgumentException("protocol == null"); this.protocol = protocol; } void setTimeouts(int readTimeoutMillis, int writeTimeoutMillis) throws RouteException { if (!connected) throw new IllegalStateException("setTimeouts - not connected"); // Don't set timeouts on shared SPDY connections. if (httpConnection != null) { try { socket.setSoTimeout(readTimeoutMillis); } catch (IOException e) { throw new RouteException(e); } httpConnection.setTimeouts(readTimeoutMillis, writeTimeoutMillis); } } void incrementRecycleCount() { recycleCount++; } /** * Returns the number of times this connection has been returned to the * connection pool. */ int recycleCount() { return recycleCount; } @Override public String toString() { return "Connection{" + route.address.uriHost + ":" + route.address.uriPort + ", proxy=" + route.proxy + " hostAddress=" + route.inetSocketAddress.getAddress().getHostAddress() + " cipherSuite=" + (handshake != null ? handshake.cipherSuite() : "none") + " protocol=" + protocol + '}'; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy