@Test public void streamedBodyIsNotRetried() throws Exception { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST)); urlFactory = new OkUrlFactory(defaultClient().newBuilder() .dns(new DoubleInetAddressDns()) .build()); HttpURLConnection connection = urlFactory.open(server.url("/").url()); connection.setDoOutput(true); connection.setChunkedStreamingMode(100); OutputStream os = connection.getOutputStream(); os.write("OutputStream is no fun.".getBytes("UTF-8")); os.close(); try { connection.getResponseCode(); fail(); } catch (IOException expected) { } assertEquals(1, server.getRequestCount()); }
@Test public void transportProblemSync() { Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .addConverterFactory(new ToStringConverterFactory()) .build(); Service example = retrofit.create(Service.class); server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); Call<String> call = example.getString(); try { call.execute(); fail(); } catch (IOException ignored) { } }
/** https://github.com/square/okhttp/issues/442 */ @Test public void tlsTimeoutsNotRetried() throws Exception { enableTls(); server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.NO_RESPONSE)); server.enqueue(new MockResponse() .setBody("unreachable!")); client = client.newBuilder() .readTimeout(100, TimeUnit.MILLISECONDS) .build(); Request request = new Request.Builder().url(server.url("/")).build(); try { // If this succeeds, too many requests were made. client.newCall(request).execute(); fail(); } catch (InterruptedIOException expected) { } }
@Test public void noRecoverWhenRetryOnConnectionFailureIsFalse() throws Exception { server.enqueue(new MockResponse().setBody("seed connection pool")); server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST)); server.enqueue(new MockResponse().setBody("unreachable!")); client = client.newBuilder() .dns(new DoubleInetAddressDns()) .retryOnConnectionFailure(false) .build(); executeSynchronously("/").assertBody("seed connection pool"); // If this succeeds, too many requests were made. executeSynchronously("/") .assertFailure(IOException.class) .assertFailureMatches("stream was reset: CANCEL", "unexpected end of stream on Connection.*" + server.getHostName() + ":" + server.getPort() + ".*"); }
@Test public void recoverFromTlsHandshakeFailure_Async() throws Exception { server.useHttps(sslClient.socketFactory, false); server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); server.enqueue(new MockResponse().setBody("abc")); client = client.newBuilder() .hostnameVerifier(new RecordingHostnameVerifier()) .connectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS)) .sslSocketFactory(suppressTlsFallbackClientSocketFactory(), sslClient.trustManager) .build(); Request request = new Request.Builder() .url(server.url("/")) .build(); client.newCall(request).enqueue(callback); callback.await(request.url()).assertBody("abc"); }
@Test public void expect100ContinueNonEmptyRequestBody() throws Exception { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.EXPECT_CONTINUE)); Request request = new Request.Builder() .url(server.url("/")) .header("Expect", "100-continue") .post(RequestBody.create(MediaType.parse("text/plain"), "abc")) .build(); executeSynchronously(request) .assertCode(200) .assertSuccessful(); assertEquals("abc", server.takeRequest().getBody().readUtf8()); }
@Test public void expect100ContinueTimesOutWithoutContinue() throws Exception { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.NO_RESPONSE)); client = client.newBuilder() .readTimeout(500, TimeUnit.MILLISECONDS) .build(); Request request = new Request.Builder() .url(server.url("/")) .header("Expect", "100-continue") .post(RequestBody.create(MediaType.parse("text/plain"), "abc")) .build(); Call call = client.newCall(request); try { call.execute(); fail(); } catch (SocketTimeoutException expected) { } RecordedRequest recordedRequest = server.takeRequest(); assertEquals("", recordedRequest.getBody().readUtf8()); }
/** * There's no read timeout when reading the first byte of a new frame. But as soon as we start * reading a frame we enable the read timeout. In this test we have the server returning the first * byte of a frame but no more frames. */ @Test public void readTimeoutAppliesWithinFrames() throws IOException { webServer.setDispatcher(new Dispatcher() { @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { return upgradeResponse(request) .setBody(new Buffer().write(ByteString.decodeHex("81"))) // Truncated frame. .removeHeader("Content-Length") .setSocketPolicy(SocketPolicy.KEEP_OPEN); } }); WebSocket webSocket = newWebSocket(); clientListener.assertOpen(); clientListener.assertFailure(SocketTimeoutException.class, "timeout"); assertFalse(webSocket.close(1000, null)); }
@Test public void recoverFromOneRefusedStreamReusesConnection() throws Exception { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.RESET_STREAM_AT_START) .setHttp2ErrorCode(ErrorCode.REFUSED_STREAM.httpCode)); server.enqueue(new MockResponse() .setBody("abc")); Call call = client.newCall(new Request.Builder() .url(server.url("/")) .build()); Response response = call.execute(); assertEquals("abc", response.body().string()); assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. assertEquals(1, server.takeRequest().getSequenceNumber()); // Reused connection. }
@Test public void recoverFromOneInternalErrorRequiresNewConnection() throws Exception { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.RESET_STREAM_AT_START) .setHttp2ErrorCode(ErrorCode.INTERNAL_ERROR.httpCode)); server.enqueue(new MockResponse() .setBody("abc")); client = client.newBuilder() .dns(new DoubleInetAddressDns()) .build(); Call call = client.newCall(new Request.Builder() .url(server.url("/")) .build()); Response response = call.execute(); assertEquals("abc", response.body().string()); assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. }
@Test public void recoverFromMultipleRefusedStreamsRequiresNewConnection() throws Exception { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.RESET_STREAM_AT_START) .setHttp2ErrorCode(ErrorCode.REFUSED_STREAM.httpCode)); server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.RESET_STREAM_AT_START) .setHttp2ErrorCode(ErrorCode.REFUSED_STREAM.httpCode)); server.enqueue(new MockResponse() .setBody("abc")); client = client.newBuilder() .dns(new DoubleInetAddressDns()) .build(); Call call = client.newCall(new Request.Builder() .url(server.url("/")) .build()); Response response = call.execute(); assertEquals("abc", response.body().string()); assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. assertEquals(1, server.takeRequest().getSequenceNumber()); // Reused connection. assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. }
private void noRecoveryFromErrorWithRetryDisabled(ErrorCode errorCode) throws Exception { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.RESET_STREAM_AT_START) .setHttp2ErrorCode(errorCode.httpCode)); server.enqueue(new MockResponse() .setBody("abc")); client = client.newBuilder() .retryOnConnectionFailure(false) .build(); Call call = client.newCall(new Request.Builder() .url(server.url("/")) .build()); try { call.execute(); fail(); } catch (StreamResetException expected) { assertEquals(errorCode, expected.errorCode); } }
@Test public void connectionNotReusedAfterShutdown() throws Exception { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END) .setBody("ABC")); server.enqueue(new MockResponse() .setBody("DEF")); Call call1 = client.newCall(new Request.Builder() .url(server.url("/")) .build()); Response response1 = call1.execute(); assertEquals("ABC", response1.body().string()); Call call2 = client.newCall(new Request.Builder() .url(server.url("/")) .build()); Response response2 = call2.execute(); assertEquals("DEF", response2.body().string()); assertEquals(0, server.takeRequest().getSequenceNumber()); assertEquals(0, server.takeRequest().getSequenceNumber()); }
@Test public void silentRetryWhenIdempotentRequestFailsOnReusedConnection() throws Exception { server.enqueue(new MockResponse().setBody("a")); server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST)); server.enqueue(new MockResponse().setBody("b")); Request request = new Request.Builder() .url(server.url("/")) .build(); Response responseA = client.newCall(request).execute(); assertEquals("a", responseA.body().string()); assertEquals(0, server.takeRequest().getSequenceNumber()); Response responseB = client.newCall(request).execute(); assertEquals("b", responseB.body().string()); assertEquals(1, server.takeRequest().getSequenceNumber()); assertEquals(0, server.takeRequest().getSequenceNumber()); }
@Test public void staleConnectionNotReusedForNonIdempotentRequest() throws Exception { server.enqueue(new MockResponse().setBody("a") .setSocketPolicy(SocketPolicy.SHUTDOWN_OUTPUT_AT_END)); server.enqueue(new MockResponse().setBody("b")); Request requestA = new Request.Builder() .url(server.url("/")) .build(); Response responseA = client.newCall(requestA).execute(); assertEquals("a", responseA.body().string()); assertEquals(0, server.takeRequest().getSequenceNumber()); // Give the socket a chance to become stale. Thread.sleep(250); Request requestB = new Request.Builder() .url(server.url("/")) .post(RequestBody.create(MediaType.parse("text/plain"), "b")) .build(); Response responseB = client.newCall(requestB).execute(); assertEquals("b", responseB.body().string()); assertEquals(0, server.takeRequest().getSequenceNumber()); }
@Test public void requestBodyRetransmittedOnClientRequestTimeout() throws Exception { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END) .setResponseCode(408) .setHeader("Connection", "Close") .setBody("You took too long!")); server.enqueue(new MockResponse().setBody("Body")); Request request = new Request.Builder() .url(server.url("/")) .post(RequestBody.create(MediaType.parse("text/plain"), "Hello")) .build(); Response response = client.newCall(request).execute(); assertEquals("Body", response.body().string()); RecordedRequest request1 = server.takeRequest(); assertEquals("Hello", request1.getBody().readUtf8()); RecordedRequest request2 = server.takeRequest(); assertEquals("Hello", request2.getBody().readUtf8()); }
@Test public void disableClientRequestTimeoutRetry() throws IOException { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END) .setResponseCode(408) .setHeader("Connection", "Close") .setBody("You took too long!")); client = client.newBuilder() .retryOnConnectionFailure(false) .build(); Request request = new Request.Builder() .url(server.url("/")) .build(); Response response = client.newCall(request).execute(); assertEquals(408, response.code()); assertEquals("You took too long!", response.body().string()); }
@Test public void maxClientRequestTimeoutRetries() throws IOException { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END) .setResponseCode(408) .setHeader("Connection", "Close") .setBody("You took too long!")); server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END) .setResponseCode(408) .setHeader("Connection", "Close") .setBody("You took too long!")); Request request = new Request.Builder() .url(server.url("/")) .build(); Response response = client.newCall(request).execute(); assertEquals(408, response.code()); assertEquals("You took too long!", response.body().string()); assertEquals(2, server.getRequestCount()); }
@Test public void successfulExpectContinuePermitsConnectionReuse() throws Exception { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.EXPECT_CONTINUE)); server.enqueue(new MockResponse()); executeSynchronously(new Request.Builder() .url(server.url("/")) .header("Expect", "100-continue") .post(RequestBody.create(MediaType.parse("text/plain"), "abc")) .build()); executeSynchronously(new Request.Builder() .url(server.url("/")) .build()); assertEquals(0, server.takeRequest().getSequenceNumber()); assertEquals(1, server.takeRequest().getSequenceNumber()); }
/** * There's no read timeout when reading the first byte of a new frame. But as soon as we start * reading a frame we enable the read timeout. In this test we have the server returning the first * byte of a frame but no more frames. */ @Test public void readTimeoutAppliesWithinFrames() throws IOException { webServer.setDispatcher(new Dispatcher() { @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { return upgradeResponse(request) .setBody(new Buffer().write(ByteString.decodeHex("81"))) // Truncated frame. .removeHeader("Content-Length") .setSocketPolicy(SocketPolicy.KEEP_OPEN); } }); WebSocket webSocket = newWebSocket(); clientListener.assertOpen(); clientListener.assertFailure(SocketTimeoutException.class, "timeout", "Read timed out"); assertFalse(webSocket.close(1000, null)); }
@Test public void multipleConnectsForSingleCall() throws IOException { enableTlsWithTunnel(false); server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); server.enqueue(new MockResponse()); client = client.newBuilder() .dns(new DoubleInetAddressDns()) .build(); Call call = client.newCall(new Request.Builder() .url(server.url("/")) .build()); Response response = call.execute(); assertEquals(200, response.code()); response.body().close(); listener.removeUpToEvent(ConnectStart.class); listener.removeUpToEvent(ConnectFailed.class); listener.removeUpToEvent(ConnectStart.class); listener.removeUpToEvent(ConnectEnd.class); }
@Test public void failedSecureConnect() { enableTlsWithTunnel(false); server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); Call call = client.newCall(new Request.Builder() .url(server.url("/")) .build()); try { call.execute(); fail(); } catch (IOException expected) { } SecureConnectStart secureStart = listener.removeUpToEvent(SecureConnectStart.class); assertSame(call, secureStart.call); CallFailed callFailed = listener.removeUpToEvent(CallFailed.class); assertSame(call, callFailed.call); assertNotNull(callFailed.ioe); }
@Test public void secureConnectWithTunnel() throws IOException { enableTlsWithTunnel(true); server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)); server.enqueue(new MockResponse()); client = client.newBuilder() .proxy(server.toProxyAddress()) .build(); Call call = client.newCall(new Request.Builder() .url(server.url("/")) .build()); Response response = call.execute(); assertEquals(200, response.code()); response.body().close(); SecureConnectStart secureStart = listener.removeUpToEvent(SecureConnectStart.class); assertSame(call, secureStart.call); SecureConnectEnd secureEnd = listener.removeUpToEvent(SecureConnectEnd.class); assertSame(call, secureEnd.call); assertNotNull(secureEnd.handshake); }
@Test public void multipleSecureConnectsForSingleCall() throws IOException { enableTlsWithTunnel(false); server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); server.enqueue(new MockResponse()); client = client.newBuilder() .dns(new DoubleInetAddressDns()) .build(); Call call = client.newCall(new Request.Builder() .url(server.url("/")) .build()); Response response = call.execute(); assertEquals(200, response.code()); response.body().close(); listener.removeUpToEvent(SecureConnectStart.class); listener.removeUpToEvent(ConnectFailed.class); listener.removeUpToEvent(SecureConnectStart.class); listener.removeUpToEvent(SecureConnectEnd.class); }
private void responseBodyFail(Protocol expectedProtocol) throws IOException { // Use a 2 MiB body so the disconnect won't happen until the client has read some data. int responseBodySize = 2 * 1024 * 1024; // 2 MiB server.enqueue(new MockResponse() .setBody(new Buffer().write(new byte[responseBodySize])) .setSocketPolicy(SocketPolicy.DISCONNECT_DURING_RESPONSE_BODY)); Call call = client.newCall(new Request.Builder() .url(server.url("/")) .build()); Response response = call.execute(); if (expectedProtocol == Protocol.HTTP_2) { // soft failure since client may not support depending on Platform assumeThat(response, matchesProtocol(Protocol.HTTP_2)); } assertEquals(expectedProtocol, response.protocol()); try { response.body.string(); fail(); } catch (IOException expected) { } CallFailed callFailed = listener.removeUpToEvent(CallFailed.class); assertNotNull(callFailed.ioe); }
@Ignore("the CallEnd event is omitted") @Test public void emptyResponseBody() throws IOException { server.enqueue(new MockResponse() .setBody("") .setBodyDelay(1, TimeUnit.SECONDS) .setSocketPolicy(SocketPolicy.DISCONNECT_DURING_RESPONSE_BODY)); Call call = client.newCall(new Request.Builder() .url(server.url("/")) .build()); Response response = call.execute(); response.body().close(); List<String> expectedEvents = Arrays.asList("CallStart", "DnsStart", "DnsEnd", "ConnectStart", "ConnectEnd", "ConnectionAcquired", "RequestHeadersStart", "RequestHeadersEnd", "ResponseHeadersStart", "ResponseHeadersEnd", "ResponseBodyStart", "ResponseBodyEnd", "ConnectionReleased", "CallEnd"); assertEquals(expectedEvents, listener.recordedEventTypes()); }
@Ignore("this reports CallFailed not CallEnd") @Test public void responseBodyClosedClosedWithoutReadingAllData() throws IOException { server.enqueue(new MockResponse() .setBody("abc") .setBodyDelay(1, TimeUnit.SECONDS) .setSocketPolicy(SocketPolicy.DISCONNECT_DURING_RESPONSE_BODY)); Call call = client.newCall(new Request.Builder() .url(server.url("/")) .build()); Response response = call.execute(); response.body().close(); List<String> expectedEvents = Arrays.asList("CallStart", "DnsStart", "DnsEnd", "ConnectStart", "ConnectEnd", "ConnectionAcquired", "RequestHeadersStart", "RequestHeadersEnd", "ResponseHeadersStart", "ResponseHeadersEnd", "ResponseBodyStart", "ResponseBodyEnd", "ConnectionReleased", "CallEnd"); assertEquals(expectedEvents, listener.recordedEventTypes()); }