Skip to content

Commit

Permalink
Use input stream as error stream when necessary (#14)
Browse files Browse the repository at this point in the history
- Use inputStream as errorStream when errored response code (as Sun intended a million years ago)
- Unit test to check error stream
  • Loading branch information
nwithan8 authored Oct 18, 2022
1 parent c3e0c76 commit a1881f0
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -706,8 +706,33 @@ public Permission getPermission() throws IOException {
*/
@Override
public InputStream getErrorStream() {
// not in cassette, get from real request
return this.connection.getErrorStream();
if (mode == Mode.Bypass) {
return this.connection.getErrorStream();
}

/*
Based on this Sun source code for HttpURLConnection (seen below):
if (connected && responseCode >= 400) {
// Client Error 4xx and Server Error 5xx
if (errorStream != null) {
return errorStream;
} else if (inputStream != null) {
return inputStream;
}
}
return null;
*/
try {
buildCache();

if (this.cachedInteraction.getResponse().getStatus().getCode() >= 400) {
// Client Error 4xx and Server Error 5xx
return createInputStream(this.cachedInteraction.getResponse().getBody());
}
return null;
} catch (VCRException | RecordingExpirationException e) {
throw new RuntimeException(e);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
import java.net.ProtocolException;
import java.net.Proxy;
import java.net.SocketPermission;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownServiceException;
import java.nio.ByteBuffer;
import java.security.Permission;
import java.security.Principal;
import java.security.cert.Certificate;
Expand Down Expand Up @@ -709,8 +711,33 @@ public Permission getPermission() throws IOException {
*/
@Override
public InputStream getErrorStream() {
// not in cassette, get from real request
return this.connection.getErrorStream();
if (mode == Mode.Bypass) {
return this.connection.getErrorStream();
}

/*
Based on this Sun source code for HttpURLConnection (seen below):
if (connected && responseCode >= 400) {
// Client Error 4xx and Server Error 5xx
if (errorStream != null) {
return errorStream;
} else if (inputStream != null) {
return inputStream;
}
}
return null;
*/
try {
buildCache();

if (this.cachedInteraction.getResponse().getStatus().getCode() >= 400) {
// Client Error 4xx and Server Error 5xx
return createInputStream(this.cachedInteraction.getResponse().getBody());
}
return null;
} catch (VCRException | RecordingExpirationException e) {
throw new RuntimeException(e);
}
}

/**
Expand Down
10 changes: 8 additions & 2 deletions src/test/java/FakeDataService.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,26 @@ public RecordableHttpsURLConnection getClient(String url) throws Exception {
throw new Exception("No VCR or client has been set.");
}

@Override
public IPAddressData getIPAddressData() throws Exception {
RecordableHttpsURLConnection client = (RecordableHttpsURLConnection) getIPAddressDataRawResponse();
String json = readFromInputStream(client.getInputStream());

return Serialization.convertJsonToObject(json, IPAddressData.class);
}

@Override
public Object getIPAddressDataRawResponse() throws Exception {
RecordableHttpsURLConnection client = getClient(URL);
client.connect();
return client;
}

public RecordableHttpsURLConnection makeBadRequest() throws Exception {
RecordableHttpsURLConnection client = getClient(URL);
client.setRequestMethod("POST");
client.connect();

return client;
}
}

private static class FakeDataServiceBase {
Expand Down
32 changes: 31 additions & 1 deletion src/test/java/HttpUrlConnectionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
import com.easypost.easyvcr.Mode;
import com.easypost.easyvcr.RecordingExpirationException;
import com.easypost.easyvcr.TimeFrame;
import com.easypost.easyvcr.Utilities;
import com.easypost.easyvcr.clients.httpurlconnection.RecordableHttpsURLConnection;
import com.google.gson.JsonParseException;
import org.junit.Assert;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
Expand All @@ -25,7 +27,7 @@

import static com.easypost.easyvcr.internalutilities.Tools.readFromInputStream;

public class HttpUrlConnectionTest {
public class HttpUrlConnectionTest {

private static FakeDataService.IPAddressData getIPAddressDataRequest(Cassette cassette, Mode mode) throws Exception {
RecordableHttpsURLConnection connection = TestUtils.getSimpleHttpsURLConnection(cassette.name, mode, null);
Expand Down Expand Up @@ -419,4 +421,32 @@ public void testExpirationSettings() throws Exception {
Assert.assertThrows(RecordingExpirationException.class, () -> HttpClients.newClient(HttpClientType.HttpsUrlConnection,
FakeDataService.URL, cassette, Mode.Replay, finalAdvancedSettings));
}

@Test
public void testReplayHttpError() throws Exception {
Cassette cassette = TestUtils.getCassette("test_replay_http_error");
cassette.erase(); // Erase cassette before recording

// make connection using Mode.Record
RecordableHttpsURLConnection connection =
(RecordableHttpsURLConnection) HttpClients.newClient(HttpClientType.HttpsUrlConnection,
FakeDataService.URL, cassette, Mode.Record);
// make data service using connection
FakeDataService.HttpsUrlConnection fakeDataService = new FakeDataService.HttpsUrlConnection(connection);
// make HTTP call with data service (record to cassette)
RecordableHttpsURLConnection clientAfterRequest = fakeDataService.makeBadRequest();
Assert.assertTrue(cassette.numInteractions() > 0); // make sure we recorded something

// make connection using Mode.Replay
connection = (RecordableHttpsURLConnection) HttpClients.newClient(HttpClientType.HttpsUrlConnection,
FakeDataService.URL, cassette, Mode.Replay);
// make data service using connection
fakeDataService = new FakeDataService.HttpsUrlConnection(connection);
// make HTTP call with data service (replay from cassette)
clientAfterRequest = fakeDataService.makeBadRequest();

// make sure the error stream was loaded properly
Assert.assertNotNull(clientAfterRequest);
Assert.assertNotNull(clientAfterRequest.getErrorStream());
}
}

0 comments on commit a1881f0

Please sign in to comment.