Skip to content

Commit a1881f0

Browse files
authored
Use input stream as error stream when necessary (#14)
- Use inputStream as errorStream when errored response code (as Sun intended a million years ago) - Unit test to check error stream
1 parent c3e0c76 commit a1881f0

File tree

4 files changed

+95
-7
lines changed

4 files changed

+95
-7
lines changed

src/main/java/com/easypost/easyvcr/clients/httpurlconnection/RecordableHttpURLConnection.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -706,8 +706,33 @@ public Permission getPermission() throws IOException {
706706
*/
707707
@Override
708708
public InputStream getErrorStream() {
709-
// not in cassette, get from real request
710-
return this.connection.getErrorStream();
709+
if (mode == Mode.Bypass) {
710+
return this.connection.getErrorStream();
711+
}
712+
713+
/*
714+
Based on this Sun source code for HttpURLConnection (seen below):
715+
if (connected && responseCode >= 400) {
716+
// Client Error 4xx and Server Error 5xx
717+
if (errorStream != null) {
718+
return errorStream;
719+
} else if (inputStream != null) {
720+
return inputStream;
721+
}
722+
}
723+
return null;
724+
*/
725+
try {
726+
buildCache();
727+
728+
if (this.cachedInteraction.getResponse().getStatus().getCode() >= 400) {
729+
// Client Error 4xx and Server Error 5xx
730+
return createInputStream(this.cachedInteraction.getResponse().getBody());
731+
}
732+
return null;
733+
} catch (VCRException | RecordingExpirationException e) {
734+
throw new RuntimeException(e);
735+
}
711736
}
712737

713738
/**

src/main/java/com/easypost/easyvcr/clients/httpurlconnection/RecordableHttpsURLConnection.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
import java.net.ProtocolException;
2424
import java.net.Proxy;
2525
import java.net.SocketPermission;
26+
import java.net.SocketTimeoutException;
2627
import java.net.URL;
2728
import java.net.UnknownServiceException;
29+
import java.nio.ByteBuffer;
2830
import java.security.Permission;
2931
import java.security.Principal;
3032
import java.security.cert.Certificate;
@@ -709,8 +711,33 @@ public Permission getPermission() throws IOException {
709711
*/
710712
@Override
711713
public InputStream getErrorStream() {
712-
// not in cassette, get from real request
713-
return this.connection.getErrorStream();
714+
if (mode == Mode.Bypass) {
715+
return this.connection.getErrorStream();
716+
}
717+
718+
/*
719+
Based on this Sun source code for HttpURLConnection (seen below):
720+
if (connected && responseCode >= 400) {
721+
// Client Error 4xx and Server Error 5xx
722+
if (errorStream != null) {
723+
return errorStream;
724+
} else if (inputStream != null) {
725+
return inputStream;
726+
}
727+
}
728+
return null;
729+
*/
730+
try {
731+
buildCache();
732+
733+
if (this.cachedInteraction.getResponse().getStatus().getCode() >= 400) {
734+
// Client Error 4xx and Server Error 5xx
735+
return createInputStream(this.cachedInteraction.getResponse().getBody());
736+
}
737+
return null;
738+
} catch (VCRException | RecordingExpirationException e) {
739+
throw new RuntimeException(e);
740+
}
714741
}
715742

716743
/**

src/test/java/FakeDataService.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,26 @@ public RecordableHttpsURLConnection getClient(String url) throws Exception {
7676
throw new Exception("No VCR or client has been set.");
7777
}
7878

79-
@Override
8079
public IPAddressData getIPAddressData() throws Exception {
8180
RecordableHttpsURLConnection client = (RecordableHttpsURLConnection) getIPAddressDataRawResponse();
8281
String json = readFromInputStream(client.getInputStream());
8382

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

87-
@Override
8886
public Object getIPAddressDataRawResponse() throws Exception {
8987
RecordableHttpsURLConnection client = getClient(URL);
9088
client.connect();
9189
return client;
9290
}
91+
92+
public RecordableHttpsURLConnection makeBadRequest() throws Exception {
93+
RecordableHttpsURLConnection client = getClient(URL);
94+
client.setRequestMethod("POST");
95+
client.connect();
96+
97+
return client;
98+
}
9399
}
94100

95101
private static class FakeDataServiceBase {

src/test/java/HttpUrlConnectionTest.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
import com.easypost.easyvcr.Mode;
1010
import com.easypost.easyvcr.RecordingExpirationException;
1111
import com.easypost.easyvcr.TimeFrame;
12+
import com.easypost.easyvcr.Utilities;
1213
import com.easypost.easyvcr.clients.httpurlconnection.RecordableHttpsURLConnection;
1314
import com.google.gson.JsonParseException;
1415
import org.junit.Assert;
1516
import org.junit.Test;
1617

1718
import java.io.IOException;
19+
import java.io.InputStream;
1820
import java.io.OutputStream;
1921
import java.net.URISyntaxException;
2022
import java.nio.charset.StandardCharsets;
@@ -25,7 +27,7 @@
2527

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

28-
public class HttpUrlConnectionTest {
30+
public class HttpUrlConnectionTest {
2931

3032
private static FakeDataService.IPAddressData getIPAddressDataRequest(Cassette cassette, Mode mode) throws Exception {
3133
RecordableHttpsURLConnection connection = TestUtils.getSimpleHttpsURLConnection(cassette.name, mode, null);
@@ -419,4 +421,32 @@ public void testExpirationSettings() throws Exception {
419421
Assert.assertThrows(RecordingExpirationException.class, () -> HttpClients.newClient(HttpClientType.HttpsUrlConnection,
420422
FakeDataService.URL, cassette, Mode.Replay, finalAdvancedSettings));
421423
}
424+
425+
@Test
426+
public void testReplayHttpError() throws Exception {
427+
Cassette cassette = TestUtils.getCassette("test_replay_http_error");
428+
cassette.erase(); // Erase cassette before recording
429+
430+
// make connection using Mode.Record
431+
RecordableHttpsURLConnection connection =
432+
(RecordableHttpsURLConnection) HttpClients.newClient(HttpClientType.HttpsUrlConnection,
433+
FakeDataService.URL, cassette, Mode.Record);
434+
// make data service using connection
435+
FakeDataService.HttpsUrlConnection fakeDataService = new FakeDataService.HttpsUrlConnection(connection);
436+
// make HTTP call with data service (record to cassette)
437+
RecordableHttpsURLConnection clientAfterRequest = fakeDataService.makeBadRequest();
438+
Assert.assertTrue(cassette.numInteractions() > 0); // make sure we recorded something
439+
440+
// make connection using Mode.Replay
441+
connection = (RecordableHttpsURLConnection) HttpClients.newClient(HttpClientType.HttpsUrlConnection,
442+
FakeDataService.URL, cassette, Mode.Replay);
443+
// make data service using connection
444+
fakeDataService = new FakeDataService.HttpsUrlConnection(connection);
445+
// make HTTP call with data service (replay from cassette)
446+
clientAfterRequest = fakeDataService.makeBadRequest();
447+
448+
// make sure the error stream was loaded properly
449+
Assert.assertNotNull(clientAfterRequest);
450+
Assert.assertNotNull(clientAfterRequest.getErrorStream());
451+
}
422452
}

0 commit comments

Comments
 (0)