View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.rest.client;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.apache.juneau.TestUtils.assertBean;
21  import static org.apache.juneau.TestUtils.assertList;
22  import static org.apache.juneau.TestUtils.assertString;
23  import static org.apache.juneau.assertions.Assertions.*;
24  import static org.apache.juneau.common.utils.IOUtils.*;
25  import static org.apache.juneau.http.HttpHeaders.*;
26  import static org.junit.jupiter.api.Assertions.*;
27  
28  import java.io.*;
29  import java.util.*;
30  import java.util.regex.*;
31  
32  import org.apache.http.*;
33  import org.apache.http.conn.*;
34  import org.apache.http.entity.*;
35  import org.apache.http.message.*;
36  import org.apache.juneau.*;
37  import org.apache.juneau.common.utils.*;
38  import org.apache.juneau.json.*;
39  import org.apache.juneau.parser.*;
40  import org.apache.juneau.rest.annotation.*;
41  import org.apache.juneau.rest.mock.*;
42  import org.apache.juneau.rest.servlet.*;
43  import org.apache.juneau.xml.*;
44  import org.junit.jupiter.api.*;
45  
46  class RestClient_Response_Body_Test extends TestBase {
47  
48  	public static class ABean {
49  		public int f;
50  		static ABean get() {
51  			var x = new ABean();
52  			x.f = 1;
53  			return x;
54  		}
55  	}
56  
57  	private static ABean bean = ABean.get();
58  
59  	@Rest
60  	public static class A extends BasicRestObject {
61  		@RestPost
62  		public InputStream echo(InputStream is) {
63  			return is;
64  		}
65  		@RestGet
66  		public ABean bean() {
67  			return bean;
68  		}
69  		@RestOp
70  		public void head() {}  // NOSONAR
71  	}
72  
73  	public static class TestClient extends MockRestClient {
74  		public HttpEntity responseEntity;
75  		public Header[] headers = {};
76  		public TestClient entity(HttpEntity entity) {
77  			this.responseEntity = entity;
78  			return this;
79  		}
80  		public TestClient headers(Header...o) {
81  			this.headers = o;
82  			return this;
83  		}
84  		public TestClient(MockRestClient.Builder builder) {
85  			super(builder);
86  		}
87  		@Override
88  		protected MockRestResponse createResponse(RestRequest request, HttpResponse httpResponse, Parser parser) throws RestCallException {
89  			var r = new BasicHttpResponse(new ProtocolVersion("http", 1,1),200,"");
90  			r.setEntity(responseEntity);
91  			for (var h : headers)
92  				r.addHeader(h);
93  			return new MockRestResponse(this, request, r, parser);
94  		}
95  	}
96  
97  	@Test void a01_basic() throws Exception {
98  		client().build().post("/echo",bean).run().assertContent().as(ABean.class).asJson().is("{f:1}");
99  		client().build().post("/echo",bean).run().assertContent().asBytes().asString().is("{f:1}");
100 	}
101 
102 	@Test void a02_overrideParser() throws Exception {
103 		var x = client().build();
104 		var b = x.post("/echo",bean).run().getContent().parser(JsonParser.DEFAULT).as(ABean.class);
105 		assertBean(b, "f", "1");
106 		assertThrowsWithMessage(Exception.class, "ParseError at [row,col]:[1,1]", ()->x.post("/echo",bean).run().getContent().parser(XmlParser.DEFAULT).as(ABean.class));
107 		assertThrowsWithMessage(Exception.class, "ParseError at [row,col]:[1,1]", ()->x.post("/echo",bean).run().getContent().parser(XmlParser.DEFAULT).assertValue().as(ABean.class));
108 	}
109 
110 	@Test void a03_asInputStream() throws Exception {
111 		var r1 = client().build().get("/bean").run();
112 		var is = r1.getContent().asInputStream();
113 		assertEquals("{f:1}", StringUtils.toUtf8(is));
114 		assertThrowsWithMessage(Exception.class, "Response has already been consumed.", ()->r1.getContent().asInputStream());
115 
116 		// Non-repeatable entity.
117 		var x = testClient().entity(inputStreamEntity("{f:2}"));
118 		var r2 = x.get("/bean").run();
119 		r2.getContent().asInputStream();
120 		assertThrowsWithMessage(Exception.class, "Response has already been consumed", ()->r2.getContent().asInputStream());
121 
122 		// Repeatable entity.
123 		x.entity(new StringEntity("{f:2}"));
124 		var r3 = x.get("/bean").run();
125 		r3.getContent().asInputStream();
126 		is = r3.getContent().asInputStream();
127 		assertEquals("{f:2}", StringUtils.toUtf8(is));
128 		is = x.get("/bean").run().getContent().asInputStream();
129 		((EofSensorInputStream)is).abortConnection();
130 
131 		var rci = new BasicRestCallInterceptor() {
132 			@Override
133 			public void onClose(RestRequest req, RestResponse res) throws Exception {
134 				throw new NullPointerException("foo");
135 			}
136 		};
137 
138 		var x2 = client().interceptors(rci).build(TestClient.class).entity(new StringEntity("{f:2}"));
139 		assertThrowsWithMessage(NullPointerException.class, "foo", ()->x2.get("/bean").run().getContent().cache().asInputStream());
140 		assertThrowsWithMessage(NullPointerException.class, "foo", ()->x2.get("/bean").run().getContent().asInputStream().close());
141 		assertThrowsWithMessage(NullPointerException.class, "foo", ()->((EofSensorInputStream)x2.get("/bean").run().getContent().asInputStream()).abortConnection());  // NOSONAR
142 	}
143 
144 	@Test void a04_asReader() throws Exception {
145 		var x = testClient();
146 		x.entity(inputStreamEntity("{f:1}"));
147 		var r = x.get("/bean").run().getContent().asReader();
148 		assertString("{f:1}", r);
149 
150 		x.entity(inputStreamEntity("{f:1}"));
151 		r = x.get("/bean").run().getContent().asReader(UTF8);
152 		assertString("{f:1}", r);
153 
154 		x.entity(inputStreamEntity("{f:1}"));
155 		r = x.get("/bean").run().getContent().asReader(null);
156 		assertString("{f:1}", r);
157 	}
158 
159 	@Test void a05_asBytes() throws Exception {
160 		var x = client().build().get("/bean").run().getContent().asBytes();
161 		assertEquals("{f:1}", StringUtils.toUtf8(x));
162 
163 		x = client().build().get("/bean").run().assertContent().asBytes().asString().is("{f:1}").getContent().asBytes();
164 		assertEquals("{f:1}", StringUtils.toUtf8(x));
165 
166 		assertThrowsWithMessage(Exception.class, "foo", ()->testClient().entity(new InputStreamEntity(badStream())).get().run().getContent().asBytes());
167 	}
168 
169 	@Test void a06_pipeTo() throws Exception {
170 		var baos = new ByteArrayOutputStream();
171 		client().build().get("/bean").run().getContent().pipeTo(baos);
172 		assertEquals("{f:1}", StringUtils.toUtf8(baos.toByteArray()));
173 
174 		var sw = new StringWriter();
175 		client().build().get("/bean").run().getContent().pipeTo(sw);
176 		assertEquals("{f:1}", sw.toString());
177 
178 		sw = new StringWriter();
179 		client().build().get("/bean").run().getContent().pipeTo(sw,UTF8);
180 		assertEquals("{f:1}", sw.toString());
181 	}
182 
183 	public static class A7a {
184 		String x;
185 		public static A7a fromReader(Reader r) throws IOException {
186 			var x = new A7a();
187 			x.x = read(r);
188 			return x;
189 		}
190 	}
191 
192 	public static class A7b {
193 		String x;
194 		public static A7b fromInputStream(InputStream is) throws IOException {
195 			var x = new A7b();
196 			x.x = read(is);
197 			return x;
198 		}
199 	}
200 
201 	public static class A7c {
202 		public A7c() {
203 			throw new RuntimeException("foo");
204 		}
205 	}
206 
207 	@Test void a07_asType() throws Exception {
208 		var x1 = testClient().entity(stringEntity("[1,2]")).get().run().getContent().as(List.class,Integer.class);
209 		assertList(x1, "1", "2");
210 
211 		var x3 = testClient().entity(stringEntity("{f:1}")).get().run().getContent().as(ABean.class);
212 		assertBean(x3, "f", "1");
213 
214 		testClient().entity(stringEntity("{f:1}")).get().run().getContent().as(ResponseContent.class);
215 
216 		var x6 = testClient().entity(stringEntity("{f:1}")).get().run().getContent().as(HttpEntity.class);
217 		assertTrue(x6 instanceof ResponseContent);
218 
219 		plainTestClient().entity(stringEntity("foo")).get().run().assertContent().as(A7a.class).is(x->x.x.equals("foo"));
220 		plainTestClient().entity(stringEntity("foo")).get().run().assertContent().as(A7b.class).is(x->x.x.equals("foo"));
221 		assertThrowsWithMessage(Exception.class, "Unsupported media-type", ()->plainTestClient().entity(stringEntity("foo")).headers(header("Content-Type","foo")).get().run().getContent().as(A7c.class));
222 		assertThrowsWithMessage(Exception.class, "foo", ()->testClient().entity(stringEntity("")).get().run().getContent().as(A7c.class));
223 
224 		var x8 = testClient().entity(stringEntity("{f:1}")).get().run().getContent().asFuture(ABean.class);
225 		assertBean(x8.get(), "f", "1");
226 
227 		var x10 = testClient().entity(stringEntity("{f:1}")).get().run().getContent().asFuture(cm(ABean.class));
228 		assertBean(x10.get(), "f", "1");
229 
230 		var x12 = testClient().entity(stringEntity("[1,2]")).get().run().getContent().asFuture(List.class,Integer.class);
231 		assertList(x12.get(), "1", "2");
232 
233 		var x14 = testClient().entity(stringEntity("{f:1}")).get().run().getContent().asString();
234 		assertEquals("{f:1}", x14);
235 
236 		assertThrowsWithMessage(Exception.class, "foo", ()->testClient().entity(new InputStreamEntity(badStream())).get().run().getContent().asString());
237 
238 		var x16 = testClient().entity(stringEntity("{f:1}")).get().run().getContent().asStringFuture();
239 		assertEquals("{f:1}", x16.get());
240 
241 		var x18 = testClient().entity(stringEntity("12345")).get().run().getContent().asAbbreviatedString(4);
242 		assertEquals("1...", x18);
243 
244 		var x20 = testClient().entity(stringEntity("{f:1}")).get().run().getContent().asObjectRest(ABean.class);
245 		assertString("1", x20.get("f"));
246 
247 		var x22 = testClient().entity(stringEntity("{f:1}")).get().run().getContent().asObjectRest();
248 		assertString("1", x22.get("f"));
249 
250 		var x24 = testClient().entity(stringEntity("foo=123")).get().run().getContent().asMatcher(Pattern.compile("foo=(.*)"));
251 		assertTrue(x24.matches());
252 		assertEquals("123", x24.group(1));
253 
254 		var x26 = testClient().entity(stringEntity("foo=123")).get().run().getContent().asMatcher("foo=(.*)");
255 		assertTrue(x26.matches());
256 		assertEquals("123", x26.group(1));
257 	}
258 
259 	//------------------------------------------------------------------------------------------------------------------
260 	// HttpEntity passthrough methods.
261 	//------------------------------------------------------------------------------------------------------------------
262 
263 	@SuppressWarnings("deprecation")
264 	@Test void b01_httpEntityMethods() throws Exception {
265 		var x1 = testClient().entity(stringEntity("foo")).get().run().getContent();
266 		assertTrue(x1.isRepeatable());
267 
268 		var x2 = testClient().entity(inputStreamEntity("foo")).get().run().getContent();
269 		assertFalse(x2.isRepeatable());
270 		assertEquals(-1L, x2.getContentLength());
271 		x2.cache().asString();
272 		assertTrue(x2.isRepeatable());
273 		assertEquals(3L, x2.getContentLength());
274 
275 		assertFalse(x2.isChunked());
276 
277 		testClient().entity(inputStreamEntity("foo")).get().run().getContent().getContentEncoding().assertValue().isNull();
278 
279 		var x3 = inputStreamEntity("foo");
280 		x3.setContentType("text/foo");
281 		x3.setContentEncoding("identity");
282 		testClient().entity(x3).get().run().getContent().response()
283 			.getContent().getContentType().assertValue().is("text/foo").response()
284 			.getContent().getContentEncoding().assertValue().is("identity");
285 
286 		var x4 = testClient().entity(inputStreamEntity("foo")).get().run().getContent().asInputStream();
287 		assertBytes(x4).asString().is("foo");
288 
289 		var x5 = new ByteArrayOutputStream();
290 		testClient().entity(inputStreamEntity("foo")).get().run().getContent().writeTo(x5);
291 		assertBytes(x5.toByteArray()).asString().is("foo");
292 
293 		assertTrue(testClient().entity(inputStreamEntity("foo")).get().run().getContent().isStreaming());
294 		assertFalse(testClient().entity(inputStreamEntity("foo")).get().run().getContent().cache().isStreaming());
295 		assertFalse(testClient().entity(stringEntity("foo")).get().run().getContent().isStreaming());
296 
297 		testClient().entity(inputStreamEntity("foo")).get().run().getContent().consumeContent();
298 	}
299 
300 	@SuppressWarnings("deprecation")
301 	@Test void b02_head() throws Exception {
302 		assertFalse(client().build().head("").run().getContent().isRepeatable());
303 		assertFalse(client().build().head("").run().getContent().isChunked());
304 		assertEquals(-1L, client().build().head("").run().getContent().getContentLength());
305 		client().build().head("").run().getContent().getContentType().assertValue().isNull();
306 		client().build().head("").run().getContent().getContentEncoding().assertValue().isNull();
307 		client().build().head("").run().getContent().writeTo(new ByteArrayOutputStream());
308 		assertFalse(client().build().head("").run().getContent().isStreaming());
309 		client().build().head("").run().getContent().consumeContent();
310 	}
311 
312 	//------------------------------------------------------------------------------------------------------------------
313 	// Helper methods.
314 	//------------------------------------------------------------------------------------------------------------------
315 
316 	private static RestClient.Builder client() {
317 		return MockRestClient.create(A.class).json5();
318 	}
319 
320 	private static TestClient plainTestClient() {
321 		return MockRestClient.create(A.class).noTrace().build(TestClient.class);
322 	}
323 
324 	private static TestClient testClient() {
325 		return MockRestClient.create(A.class).json5().noTrace().build(TestClient.class);
326 	}
327 
328 	private static StringEntity stringEntity(String in) {
329 		return new StringEntity(in, (ContentType)null);
330 	}
331 
332 	private static Header header(String name, Object val) {
333 		return basicHeader(name, val);
334 	}
335 
336 	private static InputStreamEntity inputStreamEntity(String in) {
337 		return new InputStreamEntity(inputStream(in));
338 	}
339 
340 	private static <T> ClassMeta<T> cm(Class<T> t) {
341 		 return BeanContext.DEFAULT.getClassMeta(t);
342 	}
343 
344 	private static InputStream badStream() {
345 		return new InputStream() {
346 			@Override
347 			public int read() throws IOException {
348 				throw new IOException("foo");
349 			}
350 		};
351 	}
352 }