Skip to content

Commit 67ced68

Browse files
committed
feat(gax): add StringHttpResponseParser
1 parent 1215616 commit 67ced68

2 files changed

Lines changed: 221 additions & 0 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
package com.google.api.gax.httpjson;
31+
32+
import com.google.common.io.CharStreams;
33+
import com.google.protobuf.TypeRegistry;
34+
import java.io.IOException;
35+
import java.io.InputStream;
36+
import java.io.InputStreamReader;
37+
import java.io.Reader;
38+
import java.nio.charset.StandardCharsets;
39+
import org.jspecify.annotations.NullMarked;
40+
41+
/** An {@link HttpResponseParser} that reads the HTTP response body as a UTF-8 String. */
42+
@NullMarked
43+
class StringHttpResponseParser implements HttpResponseParser<String> {
44+
45+
private static final StringHttpResponseParser INSTANCE = new StringHttpResponseParser();
46+
47+
static StringHttpResponseParser create() {
48+
return INSTANCE;
49+
}
50+
51+
private StringHttpResponseParser() {}
52+
53+
@Override
54+
public String parse(InputStream httpContent) {
55+
try (Reader reader = new InputStreamReader(httpContent, StandardCharsets.UTF_8)) {
56+
return CharStreams.toString(reader);
57+
} catch (IOException e) {
58+
throw new RestSerializationException("Failed to read response body as string", e);
59+
}
60+
}
61+
62+
@Override
63+
public String parse(InputStream httpContent, TypeRegistry registry) {
64+
return parse(httpContent);
65+
}
66+
67+
@Override
68+
public String parse(Reader httpContent, TypeRegistry registry) {
69+
try {
70+
return CharStreams.toString(httpContent);
71+
} catch (IOException e) {
72+
throw new RestSerializationException("Failed to read response body as string", e);
73+
}
74+
}
75+
76+
@Override
77+
public String serialize(String response) {
78+
return response;
79+
}
80+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
package com.google.api.gax.httpjson;
31+
32+
import static com.google.common.truth.Truth.assertThat;
33+
import static org.junit.jupiter.api.Assertions.assertThrows;
34+
35+
import com.google.protobuf.TypeRegistry;
36+
import java.io.ByteArrayInputStream;
37+
import java.io.IOException;
38+
import java.io.InputStream;
39+
import java.io.Reader;
40+
import java.io.StringReader;
41+
import java.nio.charset.StandardCharsets;
42+
import org.junit.jupiter.api.BeforeEach;
43+
import org.junit.jupiter.api.Test;
44+
45+
class StringHttpResponseParserTest {
46+
47+
private StringHttpResponseParser parser;
48+
49+
@BeforeEach
50+
void setUp() {
51+
parser = StringHttpResponseParser.create();
52+
}
53+
54+
@Test
55+
void parse_inputStream_returnsString() {
56+
String expected = "Hello, world! \u4e16\u754c";
57+
InputStream inputStream = new ByteArrayInputStream(expected.getBytes(StandardCharsets.UTF_8));
58+
59+
String result = parser.parse(inputStream);
60+
61+
assertThat(result).isEqualTo(expected);
62+
}
63+
64+
@Test
65+
void parse_inputStream_empty_returnsEmptyString() {
66+
InputStream inputStream = new ByteArrayInputStream(new byte[0]);
67+
68+
String result = parser.parse(inputStream);
69+
70+
assertThat(result).isEmpty();
71+
}
72+
73+
@Test
74+
void parse_inputStream_withTypeRegistry_returnsString() {
75+
String expected = "response content";
76+
InputStream inputStream = new ByteArrayInputStream(expected.getBytes(StandardCharsets.UTF_8));
77+
78+
String result = parser.parse(inputStream, TypeRegistry.getEmptyTypeRegistry());
79+
80+
assertThat(result).isEqualTo(expected);
81+
}
82+
83+
@Test
84+
void parse_inputStream_ioException_throwsRestSerializationException() {
85+
InputStream failingInputStream =
86+
new InputStream() {
87+
@Override
88+
public int read() throws IOException {
89+
throw new IOException("Simulated read failure");
90+
}
91+
};
92+
93+
RestSerializationException thrown =
94+
assertThrows(RestSerializationException.class, () -> parser.parse(failingInputStream));
95+
assertThat(thrown).hasCauseThat().isInstanceOf(IOException.class);
96+
}
97+
98+
@Test
99+
void parse_reader_returnsString() {
100+
String expected = "Hello from Reader!";
101+
Reader reader = new StringReader(expected);
102+
103+
String result = parser.parse(reader, TypeRegistry.getEmptyTypeRegistry());
104+
105+
assertThat(result).isEqualTo(expected);
106+
}
107+
108+
@Test
109+
void parse_reader_empty_returnsEmptyString() {
110+
Reader reader = new StringReader("");
111+
112+
String result = parser.parse(reader, TypeRegistry.getEmptyTypeRegistry());
113+
114+
assertThat(result).isEmpty();
115+
}
116+
117+
@Test
118+
void parse_reader_ioException_throwsRestSerializationException() {
119+
Reader failingReader =
120+
new Reader() {
121+
@Override
122+
public int read(char[] cbuf, int off, int len) throws IOException {
123+
throw new IOException("Simulated reader failure");
124+
}
125+
126+
@Override
127+
public void close() throws IOException {}
128+
};
129+
130+
RestSerializationException thrown =
131+
assertThrows(
132+
RestSerializationException.class,
133+
() -> parser.parse(failingReader, TypeRegistry.getEmptyTypeRegistry()));
134+
assertThat(thrown).hasCauseThat().isInstanceOf(IOException.class);
135+
}
136+
137+
@Test
138+
void serialize_returnsInputString() {
139+
assertThat(parser.serialize("hello")).isEqualTo("hello");
140+
}
141+
}

0 commit comments

Comments
 (0)