mirror of
https://github.com/square/okhttp.git
synced 2026-01-18 20:40:58 +03:00
Merge pull request #645 from square/jwilson_0311_push_socket
Push Socket into HttpConnection/SpdyConnection.
This commit is contained in:
@@ -25,16 +25,10 @@ import com.squareup.okhttp.internal.http.SpdyTransport;
|
||||
import com.squareup.okhttp.internal.spdy.SpdyConnection;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Proxy;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import okio.BufferedSink;
|
||||
import okio.BufferedSource;
|
||||
import okio.ByteString;
|
||||
import okio.Okio;
|
||||
|
||||
import static java.net.HttpURLConnection.HTTP_OK;
|
||||
import static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
|
||||
@@ -70,10 +64,6 @@ public final class Connection implements Closeable {
|
||||
private final Route route;
|
||||
|
||||
private Socket socket;
|
||||
private InputStream in;
|
||||
private OutputStream out;
|
||||
private BufferedSource source;
|
||||
private BufferedSink sink;
|
||||
private boolean connected = false;
|
||||
private HttpConnection httpConnection;
|
||||
private SpdyConnection spdyConnection;
|
||||
@@ -94,14 +84,11 @@ public final class Connection implements Closeable {
|
||||
socket = (route.proxy.type() != Proxy.Type.HTTP) ? new Socket(route.proxy) : new Socket();
|
||||
Platform.get().connectSocket(socket, route.inetSocketAddress, connectTimeout);
|
||||
socket.setSoTimeout(readTimeout);
|
||||
in = socket.getInputStream();
|
||||
out = socket.getOutputStream();
|
||||
|
||||
if (route.address.sslSocketFactory != null) {
|
||||
upgradeToTls(tunnelRequest);
|
||||
} else {
|
||||
initSourceAndSink();
|
||||
httpConnection = new HttpConnection(pool, this, source, sink);
|
||||
httpConnection = new HttpConnection(pool, this, socket);
|
||||
}
|
||||
connected = true;
|
||||
}
|
||||
@@ -128,19 +115,19 @@ public final class Connection implements Closeable {
|
||||
platform.supportTlsIntolerantServer(sslSocket);
|
||||
}
|
||||
|
||||
boolean useNpn = route.modernTls && (// Contains a spdy variant.
|
||||
route.address.protocols.contains(Protocol.HTTP_2)
|
||||
|| route.address.protocols.contains(Protocol.SPDY_3)
|
||||
);
|
||||
|
||||
if (useNpn) {
|
||||
if (route.address.protocols.contains(Protocol.HTTP_2) // Contains both spdy variants.
|
||||
&& route.address.protocols.contains(Protocol.SPDY_3)) {
|
||||
boolean useNpn = false;
|
||||
if (route.modernTls) {
|
||||
boolean http2 = route.address.protocols.contains(Protocol.HTTP_2);
|
||||
boolean spdy3 = route.address.protocols.contains(Protocol.SPDY_3);
|
||||
if (http2 && spdy3) {
|
||||
platform.setNpnProtocols(sslSocket, Protocol.HTTP2_SPDY3_AND_HTTP);
|
||||
} else if (route.address.protocols.contains(Protocol.HTTP_2)) {
|
||||
useNpn = true;
|
||||
} else if (http2) {
|
||||
platform.setNpnProtocols(sslSocket, Protocol.HTTP2_AND_HTTP_11);
|
||||
} else {
|
||||
useNpn = true;
|
||||
} else if (spdy3) {
|
||||
platform.setNpnProtocols(sslSocket, Protocol.SPDY3_AND_HTTP11);
|
||||
useNpn = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,10 +139,7 @@ public final class Connection implements Closeable {
|
||||
throw new IOException("Hostname '" + route.address.uriHost + "' was not verified");
|
||||
}
|
||||
|
||||
out = sslSocket.getOutputStream();
|
||||
in = sslSocket.getInputStream();
|
||||
handshake = Handshake.get(sslSocket.getSession());
|
||||
initSourceAndSink();
|
||||
|
||||
ByteString maybeProtocol;
|
||||
Protocol selectedProtocol = Protocol.HTTP_11;
|
||||
@@ -165,11 +149,11 @@ public final class Connection implements Closeable {
|
||||
|
||||
if (selectedProtocol.spdyVariant) {
|
||||
sslSocket.setSoTimeout(0); // SPDY timeouts are set per-stream.
|
||||
spdyConnection = new SpdyConnection.Builder(route.address.getUriHost(), true, source, sink)
|
||||
spdyConnection = new SpdyConnection.Builder(route.address.getUriHost(), true, socket)
|
||||
.protocol(selectedProtocol).build();
|
||||
spdyConnection.sendConnectionHeader();
|
||||
} else {
|
||||
httpConnection = new HttpConnection(pool, this, source, sink);
|
||||
httpConnection = new HttpConnection(pool, this, socket);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,28 +190,8 @@ public final class Connection implements Closeable {
|
||||
* #isAlive()}; callers should check {@link #isAlive()} first.
|
||||
*/
|
||||
public boolean isReadable() {
|
||||
if (source == null) {
|
||||
return true; // Optimistic.
|
||||
}
|
||||
if (isSpdy()) {
|
||||
return true; // Optimistic. We can't test SPDY because its streams are in use.
|
||||
}
|
||||
try {
|
||||
int readTimeout = socket.getSoTimeout();
|
||||
try {
|
||||
socket.setSoTimeout(1);
|
||||
if (source.exhausted()) {
|
||||
return false; // Stream is exhausted; socket is closed.
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
socket.setSoTimeout(readTimeout);
|
||||
}
|
||||
} catch (SocketTimeoutException ignored) {
|
||||
return true; // Read timed out; socket is good.
|
||||
} catch (IOException e) {
|
||||
return false; // Couldn't read; socket is closed.
|
||||
}
|
||||
if (httpConnection != null) return httpConnection.isReadable();
|
||||
return true; // SPDY connections, and connections before connect() are both optimistic.
|
||||
}
|
||||
|
||||
public void resetIdleStartTime() {
|
||||
@@ -320,9 +284,7 @@ public final class Connection implements Closeable {
|
||||
* retried if the proxy requires authorization.
|
||||
*/
|
||||
private void makeTunnel(TunnelRequest tunnelRequest) throws IOException {
|
||||
BufferedSource tunnelSource = Okio.buffer(Okio.source(in));
|
||||
BufferedSink tunnelSink = Okio.buffer(Okio.sink(out));
|
||||
HttpConnection tunnelConnection = new HttpConnection(pool, this, tunnelSource, tunnelSink);
|
||||
HttpConnection tunnelConnection = new HttpConnection(pool, this, socket);
|
||||
Request request = tunnelRequest.getRequest();
|
||||
String requestLine = tunnelRequest.requestLine();
|
||||
while (true) {
|
||||
@@ -335,7 +297,7 @@ public final class Connection implements Closeable {
|
||||
case HTTP_OK:
|
||||
// Assume the server won't send a TLS ServerHello until we send a TLS ClientHello. If that
|
||||
// happens, then we will have buffered bytes that are needed by the SSLSocket!
|
||||
if (tunnelSource.buffer().size() > 0) {
|
||||
if (tunnelConnection.bufferSize() > 0) {
|
||||
throw new IOException("TLS tunnel buffered too many bytes!");
|
||||
}
|
||||
return;
|
||||
@@ -352,9 +314,4 @@ public final class Connection implements Closeable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initSourceAndSink() throws IOException {
|
||||
source = Okio.buffer(Okio.source(in));
|
||||
sink = Okio.buffer(Okio.sink(out));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.io.OutputStream;
|
||||
import java.net.CacheRequest;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import okio.BufferedSink;
|
||||
import okio.BufferedSource;
|
||||
import okio.Deadline;
|
||||
@@ -72,18 +73,20 @@ public final class HttpConnection {
|
||||
|
||||
private final ConnectionPool pool;
|
||||
private final Connection connection;
|
||||
private final Socket socket;
|
||||
private final BufferedSource source;
|
||||
private final BufferedSink sink;
|
||||
|
||||
private int state = STATE_IDLE;
|
||||
private int onIdle = ON_IDLE_HOLD;
|
||||
|
||||
public HttpConnection(ConnectionPool pool, Connection connection, BufferedSource source,
|
||||
BufferedSink sink) {
|
||||
public HttpConnection(ConnectionPool pool, Connection connection, Socket socket)
|
||||
throws IOException {
|
||||
this.pool = pool;
|
||||
this.connection = connection;
|
||||
this.source = source;
|
||||
this.sink = sink;
|
||||
this.socket = socket;
|
||||
this.source = Okio.buffer(Okio.source(socket.getInputStream()));
|
||||
this.sink = Okio.buffer(Okio.sink(socket.getOutputStream()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,6 +126,31 @@ public final class HttpConnection {
|
||||
sink.flush();
|
||||
}
|
||||
|
||||
/** Returns the number of buffered bytes immediately readable. */
|
||||
public long bufferSize() {
|
||||
return source.buffer().size();
|
||||
}
|
||||
|
||||
/** Test for a stale socket. */
|
||||
public boolean isReadable() {
|
||||
try {
|
||||
int readTimeout = socket.getSoTimeout();
|
||||
try {
|
||||
socket.setSoTimeout(1);
|
||||
if (source.exhausted()) {
|
||||
return false; // Stream is exhausted; socket is closed.
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
socket.setSoTimeout(readTimeout);
|
||||
}
|
||||
} catch (SocketTimeoutException ignored) {
|
||||
return true; // Read timed out; socket is good.
|
||||
} catch (IOException e) {
|
||||
return false; // Couldn't read; socket is closed.
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns bytes of a request header for sending on an HTTP transport. */
|
||||
public void writeRequest(Headers headers, String requestLine) throws IOException {
|
||||
if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
|
||||
@@ -177,7 +205,6 @@ public final class HttpConnection {
|
||||
* that may never occur.
|
||||
*/
|
||||
public boolean discard(Source in, int timeoutMillis) {
|
||||
Socket socket = connection.getSocket();
|
||||
try {
|
||||
int socketTimeout = socket.getSoTimeout();
|
||||
socket.setSoTimeout(timeoutMillis);
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.squareup.okhttp.internal.Util;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -499,19 +500,18 @@ public final class SpdyConnection implements Closeable {
|
||||
private boolean client;
|
||||
|
||||
public Builder(boolean client, Socket socket) throws IOException {
|
||||
this("", client, Okio.buffer(Okio.source(socket.getInputStream())),
|
||||
Okio.buffer(Okio.sink(socket.getOutputStream())));
|
||||
this(((InetSocketAddress) socket.getRemoteSocketAddress()).getHostName(), client, socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param client true if this peer initiated the connection; false if this
|
||||
* peer accepted the connection.
|
||||
*/
|
||||
public Builder(String hostName, boolean client, BufferedSource source, BufferedSink sink) {
|
||||
public Builder(String hostName, boolean client, Socket socket) throws IOException {
|
||||
this.hostName = hostName;
|
||||
this.client = client;
|
||||
this.source = source;
|
||||
this.sink = sink;
|
||||
this.source = Okio.buffer(Okio.source(socket.getInputStream()));
|
||||
this.sink = Okio.buffer(Okio.sink(socket.getOutputStream()));
|
||||
}
|
||||
|
||||
public Builder handler(IncomingStreamHandler handler) {
|
||||
|
||||
Reference in New Issue
Block a user