Skip to content

Commit f8b2502

Browse files
committed
Issue 235: Leak in NetHttpResponse
https://codereview.appspot.com/11472043/
1 parent f10818f commit f8b2502

File tree

5 files changed

+85
-5
lines changed

5 files changed

+85
-5
lines changed

findbugs-exclude.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,9 @@
7878
<Bug pattern="BETA_CLASS_USAGE,BETA_METHOD_USAGE"/>
7979
<Class name="com.google.api.client.http.HttpRequest"/>
8080
</And>
81+
<And>
82+
<Bug pattern="MS_PKGPROTECT"/>
83+
<!-- Field should be package protected -->
84+
<Class name="com.google.api.client.testing.http.javanet.MockHttpURLConnection"/>
85+
</And>
8186
</FindBugsFilter>

google-http-client/src/main/java/com/google/api/client/http/HttpRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,7 @@ public HttpResponse execute() throws IOException {
968968
response = new HttpResponse(this, lowLevelHttpResponse);
969969
responseConstructed = true;
970970
} finally {
971-
if (!responseConstructed) {
971+
if (!responseConstructed && lowLevelHttpResponse.getContent() != null) {
972972
lowLevelHttpResponse.getContent().close();
973973
}
974974
}

google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpResponse.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
package com.google.api.client.http.javanet;
1616

17-
import com.google.api.client.http.HttpStatusCodes;
1817
import com.google.api.client.http.LowLevelHttpResponse;
1918

2019
import java.io.IOException;
@@ -57,11 +56,34 @@ public int getStatusCode() {
5756
return responseCode;
5857
}
5958

59+
/**
60+
* {@inheritDoc}
61+
*
62+
* <p>
63+
* Returns {@link HttpURLConnection#getInputStream} when it doesn't throw {@link IOException},
64+
* otherwise it returns {@link HttpURLConnection#getErrorStream}.
65+
* </p>
66+
*
67+
* <p>
68+
* Upgrade warning: in prior version 1.15 {@link #getContent()} returned
69+
* {@link HttpURLConnection#getInputStream} only when the status code was successful. Starting
70+
* with version 1.16 it returns {@link HttpURLConnection#getInputStream} when it doesn't throw
71+
* {@link IOException}, otherwise it returns {@link HttpURLConnection#getErrorStream}
72+
* </p>
73+
*/
6074
@Override
6175
public InputStream getContent() throws IOException {
62-
HttpURLConnection connection = this.connection;
63-
return HttpStatusCodes.isSuccess(responseCode)
64-
? connection.getInputStream() : connection.getErrorStream();
76+
try {
77+
return connection.getInputStream();
78+
} catch (IOException ioe) {
79+
InputStream stream = connection.getErrorStream();
80+
// if the stream is not null then the error was set on the error stream, otherwise we should
81+
// just rethrow the I/O exception.
82+
if (stream == null) {
83+
throw ioe;
84+
}
85+
return stream;
86+
}
6587
}
6688

6789
@Override

google-http-client/src/main/java/com/google/api/client/testing/http/javanet/MockHttpURLConnection.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
import com.google.api.client.util.Beta;
1818
import com.google.api.client.util.Preconditions;
1919

20+
import java.io.ByteArrayInputStream;
2021
import java.io.ByteArrayOutputStream;
2122
import java.io.IOException;
23+
import java.io.InputStream;
2224
import java.io.OutputStream;
2325
import java.net.HttpURLConnection;
2426
import java.net.URL;
@@ -47,6 +49,24 @@ public class MockHttpURLConnection extends HttpURLConnection {
4749
*/
4850
private OutputStream outputStream = new ByteArrayOutputStream(0);
4951

52+
/**
53+
* The input byte array which represents the content when the status code is less then
54+
* {@code 400}.
55+
*/
56+
public static final byte[] INPUT_BUF = new byte[1];
57+
58+
/**
59+
* The error byte array which represents the content when the status code is greater or equal to
60+
* {@code 400}.
61+
*/
62+
public static final byte[] ERROR_BUF = new byte[5];
63+
64+
/** The input stream. */
65+
private InputStream inputStream = new ByteArrayInputStream(INPUT_BUF);
66+
67+
/** The error stream. */
68+
private InputStream errorStream = new ByteArrayInputStream(ERROR_BUF);
69+
5070
/**
5171
* @param u the URL or {@code null} for none
5272
*/
@@ -109,4 +129,17 @@ public MockHttpURLConnection setResponseCode(int responseCode) {
109129
this.responseCode = responseCode;
110130
return this;
111131
}
132+
133+
@Override
134+
public InputStream getInputStream() throws IOException {
135+
if (responseCode < 400) {
136+
return inputStream;
137+
}
138+
throw new IOException();
139+
}
140+
141+
@Override
142+
public InputStream getErrorStream() {
143+
return errorStream;
144+
}
112145
}

google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpResponseTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,24 @@ public void subtestGetStatusCode(int expectedCode, int responseCode) throws IOEx
3737
assertEquals(expectedCode, new NetHttpResponse(
3838
new MockHttpURLConnection(null).setResponseCode(responseCode)).getStatusCode());
3939
}
40+
41+
public void testGetContent() throws IOException {
42+
subtestGetContent(0);
43+
subtestGetContent(200);
44+
subtestGetContent(304);
45+
subtestGetContent(307);
46+
subtestGetContent(404);
47+
subtestGetContent(503);
48+
}
49+
50+
public void subtestGetContent(int responseCode) throws IOException {
51+
NetHttpResponse response =
52+
new NetHttpResponse(new MockHttpURLConnection(null).setResponseCode(responseCode));
53+
int bytes = response.getContent().read(new byte[100]);
54+
if (responseCode < 400) {
55+
assertEquals(MockHttpURLConnection.INPUT_BUF.length, bytes);
56+
} else {
57+
assertEquals(MockHttpURLConnection.ERROR_BUF.length, bytes);
58+
}
59+
}
4060
}

0 commit comments

Comments
 (0)