void connect(int connectTimeout, int readTimeout, int writeTimeout, Request request) throws IOException { if (connected) throw new IllegalStateException("already connected"); if (route.proxy.type() != Proxy.Type.HTTP) { socket = new Socket(route.proxy); } else { socket = route.address.socketFactory.createSocket(); } socket.setSoTimeout(readTimeout); Platform.get().connectSocket(socket, route.inetSocketAddress, connectTimeout); if (route.address.sslSocketFactory != null) { upgradeToTls(request, readTimeout, writeTimeout); } else if (request != null && request.preferredProtocol() != null) { usePreferredProtocol(request); } else { httpConnection = new HttpConnection(pool, this, socket); } connected = true; }
public void connect(int connectTimeout, int readTimeout, TunnelRequest tunnelRequest) throws IOException { if (connected) throw new IllegalStateException("already connected"); if (route.proxy.type() == Proxy.Type.DIRECT || route.proxy.type() == Proxy.Type.HTTP) { socket = route.address.socketFactory.createSocket(); } else { socket = new Socket(route.proxy); } socket.setSoTimeout(readTimeout); Platform.get().connectSocket(socket, route.inetSocketAddress, connectTimeout); if (route.address.sslSocketFactory != null) { upgradeToTls(tunnelRequest); } else { httpConnection = new HttpConnection(pool, this, socket); } connected = true; }
final void makeTunnel(Request paramRequest, int paramInt1, int paramInt2) throws IOException { HttpConnection localHttpConnection = new HttpConnection(this.pool, this, this.socket); localHttpConnection.setTimeouts(paramInt1, paramInt2); URL localURL = paramRequest.url(); String str = "CONNECT " + localURL.getHost() + ":" + localURL.getPort() + " HTTP/1.1"; do { localHttpConnection.writeRequest(paramRequest.headers, str); localHttpConnection.flush(); Response.Builder localBuilder = localHttpConnection.readResponse(); localBuilder.request = paramRequest; Response localResponse = localBuilder.build(); long l = OkHeaders.contentLength(localResponse); if (l == -1L) { l = 0L; } Source localSource = localHttpConnection.newFixedLengthSource(l); Util.skipAll(localSource, 2147483647, TimeUnit.MILLISECONDS); localSource.close(); switch (localResponse.code) { default: throw new IOException("Unexpected response code for CONNECT: " + localResponse.code); case 200: if (localHttpConnection.source.buffer().size <= 0L) { break; } throw new IOException("TLS tunnel buffered too many bytes!"); case 407: paramRequest = OkHeaders.processAuthHeader(this.route.address.authenticator, localResponse, this.route.proxy); } } while (paramRequest != null); throw new IOException("Failed to authenticate with proxy"); }
private void createConnectionAccordingProtocol() throws IOException { if (protocol == Protocol.SPDY_3 || protocol == Protocol.HTTP_2) { socket.setSoTimeout(0); // SPDY timeouts are set per-stream. spdyConnection = new SpdyConnection.Builder(route.address.getUriHost(), true, socket) .protocol(protocol).build(); spdyConnection.sendConnectionPreface(); } else { httpConnection = new HttpConnection(pool, this, socket); } }
/** * To make an HTTPS connection over an HTTP proxy, send an unencrypted * CONNECT request to create the proxy connection. This may need to be * retried if the proxy requires authorization. */ private void makeTunnel(Request request, int readTimeout, int writeTimeout) throws IOException { HttpConnection tunnelConnection = new HttpConnection(pool, this, socket); tunnelConnection.setTimeouts(readTimeout, writeTimeout); URL url = request.url(); String requestLine = "CONNECT " + url.getHost() + ":" + url.getPort() + " HTTP/1.1"; while (true) { tunnelConnection.writeRequest(request.headers(), requestLine); tunnelConnection.flush(); Response response = tunnelConnection.readResponse().request(request).build(); tunnelConnection.emptyResponseBody(); switch (response.code()) { 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 (tunnelConnection.bufferSize() > 0) { throw new IOException("TLS tunnel buffered too many bytes!"); } return; case HTTP_PROXY_AUTH: request = OkHeaders.processAuthHeader( route.address.authenticator, response, route.proxy); if (request != null) continue; throw new IOException("Failed to authenticate with proxy"); default: throw new IOException( "Unexpected response code for CONNECT: " + response.code()); } } }
/** * To make an HTTPS connection over an HTTP proxy, send an unencrypted * CONNECT request to create the proxy connection. This may need to be * retried if the proxy requires authorization. */ private void makeTunnel(TunnelRequest tunnelRequest) throws IOException { HttpConnection tunnelConnection = new HttpConnection(pool, this, socket); Request request = tunnelRequest.getRequest(); String requestLine = tunnelRequest.requestLine(); while (true) { tunnelConnection.writeRequest(request.headers(), requestLine); tunnelConnection.flush(); Response response = tunnelConnection.readResponse().request(request).build(); tunnelConnection.emptyResponseBody(); switch (response.code()) { 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 (tunnelConnection.bufferSize() > 0) { throw new IOException("TLS tunnel buffered too many bytes!"); } return; case HTTP_PROXY_AUTH: request = HttpAuthenticator.processAuthHeader( route.address.authenticator, response, route.proxy); if (request != null) continue; throw new IOException("Failed to authenticate with proxy"); default: throw new IOException( "Unexpected response code for CONNECT: " + response.code()); } } }
/** * Create an {@code SSLSocket} and perform the TLS handshake and certificate * validation. */ private void upgradeToTls(TunnelRequest tunnelRequest) throws IOException { Platform platform = Platform.get(); // Make an SSL Tunnel on the first message pair of each SSL + proxy connection. if (requiresTunnel()) { makeTunnel(tunnelRequest); } // Create the wrapper over connected socket. socket = route.address.sslSocketFactory .createSocket(socket, route.address.uriHost, route.address.uriPort, true /* autoClose */); SSLSocket sslSocket = (SSLSocket) socket; if (route.modernTls) { platform.enableTlsExtensions(sslSocket, route.address.uriHost); } else { platform.supportTlsIntolerantServer(sslSocket); } 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); useNpn = true; } else if (http2) { platform.setNpnProtocols(sslSocket, Protocol.HTTP2_AND_HTTP_11); useNpn = true; } else if (spdy3) { platform.setNpnProtocols(sslSocket, Protocol.SPDY3_AND_HTTP11); useNpn = true; } } // Force handshake. This can throw! sslSocket.startHandshake(); // Verify that the socket's certificates are acceptable for the target host. if (!route.address.hostnameVerifier.verify(route.address.uriHost, sslSocket.getSession())) { throw new IOException("Hostname '" + route.address.uriHost + "' was not verified"); } handshake = Handshake.get(sslSocket.getSession()); ByteString maybeProtocol; Protocol selectedProtocol = Protocol.HTTP_11; if (useNpn && (maybeProtocol = platform.getNpnSelectedProtocol(sslSocket)) != null) { selectedProtocol = Protocol.find(maybeProtocol); // Throws IOE on unknown. } if (selectedProtocol.spdyVariant) { sslSocket.setSoTimeout(0); // SPDY timeouts are set per-stream. spdyConnection = new SpdyConnection.Builder(route.address.getUriHost(), true, socket) .protocol(selectedProtocol).build(); spdyConnection.sendConnectionHeader(); } else { httpConnection = new HttpConnection(pool, this, socket); } }