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.commons.utils.CollectionUtils.*;
21  import static org.apache.juneau.commons.utils.ThrowableUtils.*;
22  import static org.apache.juneau.http.HttpHeaders.*;
23  import static org.apache.juneau.http.HttpResponses.*;
24  import static org.apache.juneau.junit.bct.BctAssertions.*;
25  import static org.junit.jupiter.api.Assertions.*;
26  
27  import java.io.*;
28  import java.util.concurrent.*;
29  import java.util.logging.*;
30  
31  import org.apache.http.*;
32  import org.apache.http.protocol.*;
33  import org.apache.juneau.*;
34  import org.apache.juneau.annotation.*;
35  import org.apache.juneau.http.annotation.*;
36  import org.apache.juneau.http.annotation.Header;
37  import org.apache.juneau.http.response.*;
38  import org.apache.juneau.httppart.*;
39  import org.apache.juneau.json.*;
40  import org.apache.juneau.marshaller.*;
41  import org.apache.juneau.rest.annotation.*;
42  import org.apache.juneau.rest.httppart.*;
43  import org.apache.juneau.rest.mock.*;
44  import org.apache.juneau.rest.servlet.*;
45  import org.apache.juneau.utest.utils.*;
46  import org.apache.juneau.xml.*;
47  import org.junit.jupiter.api.*;
48  
49  class RestClient_Config_RestClient_Test extends TestBase {
50  
51  	public static class ABean {
52  		public int f;
53  		static ABean get() {
54  			var x = new ABean();
55  			x.f = 1;
56  			return x;
57  		}
58  		@Override
59  		public String toString() {
60  			return Json5.of(this);
61  		}
62  	}
63  
64  	private static ABean bean = ABean.get();
65  
66  	@Rest
67  	public static class A extends BasicRestObject {
68  		@RestOp(path="/bean")
69  		public ABean getBean() {
70  			return bean;
71  		}
72  		@RestOp(path="/bean")
73  		public ABean postBean(@Content ABean b) {
74  			return b;
75  		}
76  		@RestOp(path="/echo/*")
77  		public String getEcho(org.apache.juneau.rest.RestRequest req) {
78  			return req.toString();
79  		}
80  		@RestOp(path="/echoBody")
81  		public Reader postEchoBody(org.apache.juneau.rest.RestRequest req) throws IOException {
82  			return req.getContent().getReader();
83  		}
84  		@RestOp(path="/ok")
85  		public Ok getOk() {
86  			return OK;
87  		}
88  		@RestOp(path="/checkHeader")
89  		public String[] getHeader(org.apache.juneau.rest.RestRequest req) {
90  			return req.getHeaders().getAll(req.getHeaderParam("Check").orElse(null)).stream().map(RequestHeader::getValue).toArray(String[]::new);
91  		}
92  	}
93  
94  	public static class A1 extends BasicRestCallHandler {
95  		public A1(RestClient client) {
96  			super(client);
97  		}
98  		@Override
99  		public HttpResponse run(HttpHost target, HttpRequest request, HttpContext context) throws IOException {
100 			request.addHeader("Check","Foo");
101 			request.addHeader("Foo","baz");
102 			return super.run(target,request,context);
103 		}
104 	}
105 
106 	@Test void a01_callHandler() throws Exception {
107 		client().callHandler(A1.class).header("Foo","f1").build().get("/checkHeader").header("Foo","f2").run().assertContent("['f1','f2','baz']");
108 	}
109 
110 	@Test void a02_errorCodes() {
111 		var x1 = client().errorCodes(x -> x == 200).build();
112 		var x2 = client().build();
113 		assertEquals(200, ((RestCallException)assertThrows(Throwable.class, ()->x1.get("/echo").run())).getResponseCode());
114 		assertEquals(200, ((RestCallException)assertThrows(Throwable.class, ()->x2.get("/echo").errorCodes(x -> x == 200).run())).getResponseCode());
115 	}
116 
117 	@Test void a03_executorService() throws Exception {
118 		var es = new ThreadPoolExecutor(1,1,30,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10));
119 		var x1 = client().executorService(es,true).build();
120 
121 		assertEquals(es,x1.getExecutorService());
122 		x1.get("/echo").runFuture().get().assertStatus(200).assertContent().isContains("GET /echo HTTP/1.1");
123 
124 		es = null;
125 		var x2 = client().executorService(es,true).build();
126 		assertNotNull(x2.getExecutorService());
127 		x2.get("/echo").runFuture().get().assertStatus(200).assertContent().isContains("GET /echo HTTP/1.1");
128 	}
129 
130 	@Test void a04_keepHttpClientOpen() throws Exception {
131 		var x = client().keepHttpClientOpen().build();
132 
133 		var c = x.httpClient;
134 		x.close();
135 		client().httpClient(c).build().get("/ok").runFuture().get().assertContent().isContains("OK");
136 
137 		x = client().keepHttpClientOpen().build();
138 		c = x.httpClient;
139 		x.close();
140 		client().httpClient(c).build().get("/ok").runFuture().get().assertContent().isContains("OK");
141 	}
142 
143 	public static class A5 extends BasicRestCallInterceptor {
144 		public static int x;
145 		@Override
146 		public void onInit(RestRequest req) throws Exception {
147 			x = 1;
148 			req.header("Foo","f2");
149 		}
150 		@Override
151 		public void onConnect(RestRequest req, RestResponse res) throws Exception {
152 			x += 10;
153 			res.addHeader("Bar","b1");
154 		}
155 		@Override
156 		public void onClose(RestRequest req, RestResponse res) throws Exception {
157 			x += 100;
158 		}
159 	}
160 
161 	public static class A5a extends BasicRestCallInterceptor {
162 		@Override
163 		public void onInit(RestRequest req) throws Exception {
164 			throw new RuntimeException("foo");
165 		}
166 	}
167 
168 	public static class A5b extends BasicRestCallInterceptor {
169 		@Override
170 		public void onInit(RestRequest req) throws Exception {}  // NOSONAR
171 		@Override
172 		public void onConnect(RestRequest req, RestResponse res) throws Exception {
173 			throw new RuntimeException("foo");
174 		}
175 		@Override
176 		public void onClose(RestRequest req, RestResponse res) throws Exception {}  // NOSONAR
177 	}
178 
179 	public static class A5c extends BasicRestCallInterceptor {
180 		@Override
181 		public void onInit(RestRequest req) throws Exception {}  // NOSONAR
182 		@Override
183 		public void onConnect(RestRequest req, RestResponse res) throws Exception {}  // NOSONAR
184 		@Override
185 		public void onClose(RestRequest req, RestResponse res) throws Exception {
186 			throw new RuntimeException("foo");
187 		}
188 	}
189 
190 	public static class A5d extends BasicRestCallInterceptor {
191 		@Override
192 		public void onInit(RestRequest req) throws Exception {
193 			throw new RestCallException(null,null,"foo");
194 		}
195 	}
196 
197 	public static class A5e extends BasicRestCallInterceptor {
198 		@Override
199 		public void onInit(RestRequest req) throws Exception {}  // NOSONAR
200 		@Override
201 		public void onConnect(RestRequest req, RestResponse res) throws Exception {
202 			throw new RestCallException(null,null,"foo");
203 		}
204 		@Override
205 		public void onClose(RestRequest req, RestResponse res) throws Exception {}  // NOSONAR
206 	}
207 
208 	public static class A5f extends BasicRestCallInterceptor {
209 		@Override
210 		public void onInit(RestRequest req) throws Exception {}  // NOSONAR
211 		@Override
212 		public void onConnect(RestRequest req, RestResponse res) throws Exception {}  // NOSONAR
213 		@Override
214 		public void onClose(RestRequest req, RestResponse res) throws Exception {
215 			throw new RuntimeException("foo");
216 		}
217 	}
218 
219 	public static class A5g extends BasicRestCallInterceptor {
220 		@Override
221 		public void onInit(RestRequest req) throws Exception {
222 			throw new IOException("foo");
223 		}
224 	}
225 
226 	public static class A5h extends BasicRestCallInterceptor {
227 		@Override
228 		public void onInit(RestRequest req) throws Exception {}  // NOSONAR
229 		@Override
230 		public void onConnect(RestRequest req, RestResponse res) throws Exception {
231 			throw new IOException("foo");
232 		}
233 		@Override
234 		public void onClose(RestRequest req, RestResponse res) throws Exception {}  // NOSONAR
235 	}
236 
237 	public static class A5i extends BasicRestCallInterceptor {
238 		@Override
239 		public void onInit(RestRequest req) throws Exception {}  // NOSONAR
240 		@Override
241 		public void onConnect(RestRequest req, RestResponse res) throws Exception {}  // NOSONAR
242 		@Override
243 		public void onClose(RestRequest req, RestResponse res) throws Exception {
244 			throw new RuntimeException("foo");
245 		}
246 	}
247 
248 	@Test void a05_interceptors() throws Exception {
249 		client().header("Foo","f1").interceptors(A5.class).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run().assertContent("['f1','f2','f3']").assertHeader("Bar").is("b1");
250 		assertEquals(111,A5.x);
251 
252 		client().header("Foo","f1").interceptors(new A5()).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run().assertContent("['f1','f2','f3']").assertHeader("Bar").is("b1");
253 		assertEquals(111,A5.x);
254 
255 		client().header("Foo","f1").build().get("/checkHeader").interceptors(new A5()).header("Check","foo").header("Foo","f3").run().assertContent("['f1','f2','f3']").assertHeader("Bar").is("b1");
256 		assertEquals(111,A5.x);
257 
258 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").interceptors(A5a.class).build().get("/checkHeader"));
259 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").interceptors(A5b.class).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run());
260 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").interceptors(A5c.class).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run().close());
261 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").interceptors(new A5a()).build().get("/checkHeader"));
262 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").interceptors(new A5b()).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run());
263 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").interceptors(new A5c()).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run().close());
264 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").build().get("/checkHeader").interceptors(new A5a()));
265 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").build().get("/checkHeader").interceptors(new A5b()).header("Check","foo").header("Foo","f3").run());
266 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").build().get("/checkHeader").interceptors(new A5c()).header("Check","foo").header("Foo","f3").run().close());
267 
268 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").interceptors(A5d.class).build().get("/checkHeader"));
269 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").interceptors(A5e.class).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run());
270 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").interceptors(A5f.class).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run().close());
271 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").interceptors(new A5d()).build().get("/checkHeader"));
272 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").interceptors(new A5e()).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run());
273 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").interceptors(new A5f()).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run().close());
274 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").build().get("/checkHeader").interceptors(new A5d()));
275 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").build().get("/checkHeader").interceptors(new A5e()).header("Check","foo").header("Foo","f3").run());
276 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").build().get("/checkHeader").interceptors(new A5f()).header("Check","foo").header("Foo","f3").run().close());
277 
278 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").interceptors(A5g.class).build().get("/checkHeader"));
279 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").interceptors(A5h.class).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run());
280 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").interceptors(A5i.class).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run().close());
281 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").interceptors(new A5g()).build().get("/checkHeader"));
282 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").interceptors(new A5h()).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run());
283 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").interceptors(new A5i()).build().get("/checkHeader").header("Check","foo").header("Foo","f3").run().close());
284 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").build().get("/checkHeader").interceptors(new A5g()));
285 		assertThrowsWithMessage(RestCallException.class, "foo", ()->client().header("Foo","f1").build().get("/checkHeader").interceptors(new A5h()).header("Check","foo").header("Foo","f3").run());
286 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().header("Foo","f1").build().get("/checkHeader").interceptors(new A5i()).header("Check","foo").header("Foo","f3").run().close());
287 
288 		assertThrowsWithMessage(ConfigException.class, "Invalid class of type 'java.lang.String' passed to interceptors().", ()->client().interceptors(String.class));
289 		assertThrowsWithMessage(ConfigException.class, "Invalid object of type 'java.lang.String' passed to interceptors().", ()->client().interceptors(""));
290 		client().interceptors((Object)null).header("Foo","f1").build().get("/checkHeader");
291 		client().interceptors((Class<?>)null).header("Foo","f1").build().get("/checkHeader");
292 	}
293 
294 	public static class A6a extends BasicRestCallInterceptor {
295 		@Override /* RestCallInterceptor */
296 		public void onInit(RestRequest req) throws Exception { throw new RuntimeException("foo"); }
297 	}
298 	public static class A6b extends BasicRestCallInterceptor {
299 		@Override /* RestCallInterceptor */
300 		public void onConnect(RestRequest req, RestResponse res) throws Exception { throw new RuntimeException("foo"); }
301 	}
302 	public static class A6c extends BasicRestCallInterceptor {
303 		@Override /* RestCallInterceptor */
304 		public void onClose(RestRequest req, RestResponse res) throws Exception { throw new RuntimeException("foo"); }
305 	}
306 
307 	@Test void a06_interceptors_exceptionHandling() {
308 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().interceptors(A6a.class).build().post("/bean",bean).complete());
309 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().interceptors(A6b.class).build().post("/bean",bean).complete());
310 		assertThrowsWithMessage(RuntimeException.class, "foo", ()->client().interceptors(A6c.class).build().post("/bean",bean).complete());
311 	}
312 
313 	public static class A7 extends RestClient {
314 		private static String lastMessage;
315 		public A7(RestClient.Builder builder) {
316 			super(builder);
317 		}
318 		@Override
319 		public void log(Level level,String msg, Object...args) {
320 			lastMessage = msg;
321 		}
322 	}
323 
324 	@Test void a07_leakDetection() throws Throwable {
325 		client().detectLeaks().build(A7.class).finalize();
326 		assertEquals("WARNING:  RestClient garbage collected before it was finalized.",A7.lastMessage);
327 	}
328 
329 	@Test void a08_marshaller() throws Exception {
330 		var rc = MockRestClient.create(A.class).marshaller(Xml.DEFAULT).build();
331 		var b = rc.post("/echoBody",bean).run().cacheContent().assertContent("<object><f>1</f></object>").getContent().as(ABean.class);
332 		assertEquals(json(bean), json(b));
333 	}
334 
335 	@Test void a09_marshalls() throws Exception {
336 		var x = MockRestClient.create(A.class).marshallers(Xml.DEFAULT,Json.DEFAULT).build();
337 
338 		assertThrowsWithMessage(RestCallException.class, "Content-Type not specified on request.  Cannot match correct serializer.  Use contentType(String) or mediaType(String) to specify transport language.", ()->x.post("/echoBody",bean).run());
339 
340 		assertThrowsWithMessage(Exception.class, "Content-Type not specified in response header.  Cannot find appropriate parser.", ()->x.post("/echoBody",bean).contentType("text/json").run().getContent().as(ABean.class));
341 
342 		var b = x.post("/echoBody",bean).accept("text/xml").contentType("text/xml").run().cacheContent().assertContent("<object><f>1</f></object>").getContent().as(ABean.class);
343 		assertEquals(json(bean), json(b));
344 
345 		b = x.post("/echoBody",bean).mediaType("text/xml").run().cacheContent().assertContent("<object><f>1</f></object>").getContent().as(ABean.class);
346 		assertEquals(json(bean), json(b));
347 
348 		b = x.post("/echoBody",bean).accept("text/json").contentType("text/json").run().cacheContent().assertContent("{\"f\":1}").getContent().as(ABean.class);
349 		assertEquals(json(bean), json(b));
350 
351 		b = x.post("/echoBody",bean).mediaType("text/json").run().cacheContent().assertContent("{\"f\":1}").getContent().as(ABean.class);
352 		assertEquals(json(bean), json(b));
353 	}
354 
355 	@Test void a10_serializer_parser() throws Exception {
356 		var x = MockRestClient.create(A.class).serializer(XmlSerializer.class).parser(XmlParser.class).build();
357 
358 		var b = x.post("/echoBody",bean).run().cacheContent().assertContent("<object><f>1</f></object>").getContent().as(ABean.class);
359 		assertEquals(json(bean), json(b));
360 
361 		x = MockRestClient.create(A.class).serializer(XmlSerializer.DEFAULT).parser(XmlParser.DEFAULT).build();
362 		b = x.post("/echoBody",bean).run().cacheContent().assertContent("<object><f>1</f></object>").getContent().as(ABean.class);
363 		assertEquals(json(bean), json(b));
364 	}
365 
366 	@Test void a11_serializers_parsers() throws Exception {
367 		var x = MockRestClient.create(A.class).serializers(a(XmlSerializer.class,JsonSerializer.class)).parsers(a(XmlParser.class,JsonParser.class)).build();
368 
369 		assertThrowsWithMessage(RestCallException.class, "Content-Type not specified on request.  Cannot match correct serializer.  Use contentType(String) or mediaType(String) to specify transport language.", ()->x.post("/echoBody",bean).run());
370 
371 		assertThrowsWithMessage(Exception.class, "Content-Type not specified in response header.  Cannot find appropriate parser.", ()->x.post("/echoBody",bean).contentType("text/json").run().getContent().as(ABean.class));
372 
373 		var b = x.post("/echoBody",bean).accept("text/xml").contentType("text/xml").run().cacheContent().assertContent("<object><f>1</f></object>").getContent().as(ABean.class);
374 		assertEquals(json(bean), json(b));
375 
376 		b = x.post("/echoBody",bean).mediaType("text/xml").run().cacheContent().assertContent("<object><f>1</f></object>").getContent().as(ABean.class);
377 		assertEquals(json(bean), json(b));
378 
379 		b = x.post("/echoBody",bean).accept("text/json").contentType("text/json").run().cacheContent().assertContent("{\"f\":1}").getContent().as(ABean.class);
380 		assertEquals(json(bean), json(b));
381 
382 		b = x.post("/echoBody",bean).mediaType("text/json").run().cacheContent().assertContent("{\"f\":1}").getContent().as(ABean.class);
383 		assertEquals(json(bean), json(b));
384 
385 		var x2 = MockRestClient.create(A.class).serializers(XmlSerializer.DEFAULT,JsonSerializer.DEFAULT).parsers(XmlParser.DEFAULT,JsonParser.DEFAULT).build();
386 
387 		assertThrowsWithMessage(RestCallException.class, "Content-Type not specified on request.  Cannot match correct serializer.  Use contentType(String) or mediaType(String) to specify transport language.", ()->x2.post("/echoBody",bean).run());
388 
389 		assertThrowsWithMessage(Exception.class, "Content-Type not specified in response header.  Cannot find appropriate parser.", ()->x2.post("/echoBody",bean).contentType("text/json").run().getContent().as(ABean.class));
390 
391 		b = x2.post("/echoBody",bean).accept("text/xml").contentType("text/xml").run().cacheContent().assertContent("<object><f>1</f></object>").getContent().as(ABean.class);
392 		assertEquals(json(bean), json(b));
393 
394 		b = x2.post("/echoBody",bean).mediaType("text/xml").run().cacheContent().assertContent("<object><f>1</f></object>").getContent().as(ABean.class);
395 		assertEquals(json(bean), json(b));
396 
397 		b = x2.post("/echoBody",bean).accept("text/json").contentType("text/json").run().cacheContent().assertContent("{\"f\":1}").getContent().as(ABean.class);
398 		assertEquals(json(bean), json(b));
399 
400 		b = x2.post("/echoBody",bean).mediaType("text/json").run().cacheContent().assertContent("{\"f\":1}").getContent().as(ABean.class);
401 		assertEquals(json(bean), json(b));
402 	}
403 
404 	@Rest(partSerializer=A12a.class,partParser=A12b.class)
405 	public static class A12 extends BasicRestObject {
406 		@RestGet(path="/")
407 		public Ok get(@Header(name="Foo") @Schema(cf="multi") ABean[] foo,org.apache.juneau.rest.RestRequest req,org.apache.juneau.rest.RestResponse res) throws Exception {
408 			assertEquals(2,foo.length);
409 			assertList(req.getHeaders().getAll("Foo").stream().map(RequestHeader::getValue).toList(), "x{f:1}", "x{f:1}");
410 			assertEquals("{f:1}",foo[0].toString());
411 			assertEquals("{f:1}",foo[1].toString());
412 			res.setHeader("Foo",bean);
413 			return OK;
414 		}
415 	}
416 
417 	public static class A12a extends FakeWriterSerializer {
418 		public A12a(Builder builder) {
419 			super(builder.partFunction((t,s,o)->"x" + Json5.of(o)));
420 		}
421 	}
422 
423 	public static class A12b extends FakeReaderParser {
424 		public A12b(Builder builder) {
425 			super(builder.partFunction(A12b::in));
426 		}
427 
428 		private static Object in(HttpPartType type, HttpPartSchema schema, String in, ClassMeta<?> c) {
429 			try {
430 				if (c.isParentOf(ABean.class))
431 					return Json5.DEFAULT.read(in.substring(1),c);
432 				return SimplePartParser.DEFAULT.parse(type,schema,in,c);
433 			} catch (Exception e) {
434 				throw toRex(e);
435 			}
436 		}
437 	}
438 
439 	@Test void a12_partSerializer_partParser() throws Exception {
440 		var x = client(A12.class).headers(serializedHeader("Foo", bean)).partSerializer(A12a.class).partParser(A12b.class).build();
441 		var b = x.get("/").header("Foo",bean).run().assertHeader("Foo").is("x{f:1}").getHeader("Foo").as(ABean.class).get();
442 		assertEquals("{f:1}",b.toString());
443 		b = x.get().header("Foo",bean).run().assertHeader("Foo").is("x{f:1}").getHeader("Foo").as(ABean.class).get();
444 		assertEquals("{f:1}",b.toString());
445 
446 		x = client(A12.class).headers(serializedHeader("Foo", bean)).partSerializer(new A12a(FakeWriterSerializer.create())).partParser(new A12b(FakeReaderParser.create())).build();
447 		b = x.get("/").header("Foo",bean).run().assertHeader("Foo").is("x{f:1}").getHeader("Foo").as(ABean.class).get();
448 		assertEquals("{f:1}",b.toString());
449 	}
450 
451 	@Test void a13_toString() {
452 		var s = client().rootUrl("https://foo").build().toString();
453 		assertTrue(s.contains("rootUrl=https://foo"));
454 	}
455 
456 	@Test void a14_request_target() throws Exception {
457 		client().build().get("/bean").target(new HttpHost("localhost")).run().assertContent("{f:1}");
458 	}
459 
460 	@Test void a15_request_context() throws Exception {
461 		client().build().get("/bean").context(new BasicHttpContext()).run().assertContent("{f:1}");
462 	}
463 
464 	@Test void a16_request_uriParts() throws Exception {
465 		var uri = client().build().get().uriScheme("http").uriHost("localhost").uriPort(8080).uriUserInfo("foo:bar").uri("/bean").uriFragment("baz").queryData("foo","bar").run().assertContent("{f:1}").getRequest().getURI();
466 		assertEquals("http://foo:bar@localhost:8080/bean?foo=bar#baz",uri.toString());
467 
468 		uri = client().build().get().uriScheme("http").uriHost("localhost").uriPort(8080).uriUserInfo("foo","bar").uri("/bean").uriFragment("baz").queryData("foo","bar").run().assertContent("{f:1}").getRequest().getURI();
469 		assertEquals("http://foo:bar@localhost:8080/bean?foo=bar#baz",uri.toString());
470 
471 		uri = client().build().get().uri("http://localhost").uri("http://foo:bar@localhost:8080/bean?foo=bar#baz").run().assertContent("{f:1}").getRequest().getURI();
472 		assertEquals("http://foo:bar@localhost:8080/bean?foo=bar#baz",uri.toString());
473 
474 		uri = client().build().get().uri(new java.net.URI(null,null,null,null)).uri(new java.net.URI("http://foo:bar@localhost:8080/bean?foo=bar#baz")).run().assertContent("{f:1}").getRequest().getURI();
475 		assertEquals("http://foo:bar@localhost:8080/bean?foo=bar#baz",uri.toString());
476 	}
477 
478 	//------------------------------------------------------------------------------------------------------------------
479 	// Helper methods.
480 	//------------------------------------------------------------------------------------------------------------------
481 
482 	private static RestClient.Builder client() {
483 		return MockRestClient.create(A.class).json5();
484 	}
485 
486 	private static RestClient.Builder client(Class<?> c) {
487 		return MockRestClient.create(c).json5();
488 	}
489 }