001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest.mock2; 014 015import static org.apache.juneau.internal.StringUtils.*; 016import static org.apache.juneau.rest.util.RestUtils.*; 017 018import java.io.*; 019import java.net.*; 020import java.util.*; 021import java.util.concurrent.*; 022import java.util.zip.*; 023 024import javax.servlet.http.*; 025 026import org.apache.http.*; 027import org.apache.http.client.methods.*; 028import org.apache.http.entity.*; 029import org.apache.http.message.*; 030import org.apache.juneau.*; 031import org.apache.juneau.http.remote.*; 032import org.apache.juneau.parser.*; 033import org.apache.juneau.rest.*; 034import org.apache.juneau.rest.annotation.*; 035import org.apache.juneau.rest.client2.*; 036import org.apache.juneau.rest.client2.RestRequest; 037 038/** 039 * Mocked {@link RestClient}. 040 * 041 * <p> 042 * This class is used for performing serverless unit testing of {@link Rest @Rest}-annotated and {@link Remote @Remote}-annotated classes. 043 * 044 * <p> 045 * The class itself extends from {@link RestClient} providing it with the rich feature set of that API and combines 046 * it with the Apache HttpClient {@link HttpClientConnection} interface for processing requests. 047 * The class converts {@link HttpRequest} objects to instances of {@link MockServletRequest} and {@link MockServletResponse} which are passed directly 048 * to the call handler on the resource class {@link RestContext#execute(HttpServletRequest,HttpServletResponse)}. 049 * In effect, you're fully testing your REST API as if it were running in a live servlet container, yet not 050 * actually having to run in a servlet container. 051 * All aspects of the client and server side code are tested, yet no servlet container is required. The actual 052 * over-the-wire transmission is the only aspect being bypassed. 053 * 054 * <p> 055 * The following shows a simple example of invoking a PUT method on a simple REST interface and asserting the correct status code and response body: 056 * 057 * <h5 class='figure'>Example:</h5> 058 * <p class='bcode w800'> 059 * <jk>public class</jk> MockTest { 060 * 061 * <jc>// A simple bean with one field.</jc> 062 * <jk>public static class</jk> MyBean { 063 * <jk>public int</jk> <jf>foo</jf> = 1; 064 * } 065 * 066 * <jc>// Our REST resource to test.</jc> 067 * <jc>// Simply echos the response.</jc> 068 * <ja>@Rest</ja>( 069 * serializers=SimpleJsonSerializer.<jk>class</jk>, 070 * parsers=JsonParser.<jk>class</jk> 071 * ) 072 * <jk>public static class</jk> EchoRest { 073 * 074 * <ja>@RestMethod</ja>( 075 * name=<jsf>PUT</jsf>, 076 * path=<js>"/echo"</js> 077 * ) 078 * <jk>public</jk> MyBean echo(<ja>@Body</ja> MyBean bean) { 079 * <jk>return</jk> bean; 080 * } 081 * } 082 * 083 * <jc>// Our JUnit test.</jc> 084 * <ja>@Test</ja> 085 * <jk>public void</jk> testEcho() <jk>throws</jk> Exception { 086 * 087 * MyBean myBean = <jk>new</jk> MyBean(); 088 * 089 * <jc>// Do a round-trip on the bean through the REST interface</jc> 090 * myBean = MockRestClient 091 * .<jsm>create</jsm>(EchoRest.<jk>class</jk>) 092 * .simpleJson() 093 * .build() 094 * .put(<js>"/echo"</js>, myBean) 095 * .run() 096 * .assertStatus().is(200) 097 * .assertBody().is(<js>"{foo:1}"</js>) 098 * .getBody().as(MyBean.<jk>class</jk>); 099 * 100 * <jsm>assertEquals</jsm>(1, myBean.<jf>foo</jf>); 101 * } 102 * } 103 * </p> 104 * <p> 105 * Breaking apart the fluent method call above will help you understand how this works. 106 * 107 * <p class='bcode w800'> 108 * <ja>@Test</ja> 109 * <jk>public void</jk> testEcho() <jk>throws</jk> Exception { 110 * 111 * <jc>// Instantiate our mock client.</jc> 112 * MockRestClient client = MockRestClient 113 * .<jsm>create</jsm>(EchoRest.<jk>class</jk>) 114 * .simpleJson() 115 * .build(); 116 * 117 * <jc>// Create a request.</jc> 118 * RestRequest req = client.put(<js>"/echo"</js>, myBean); 119 * 120 * <jc>// Execute it (by calling RestCallHandler.service(...) and then returning the response object).</jc> 121 * RestResponse res = req.run(); 122 * 123 * <jc>// Run assertion tests on the results.</jc> 124 * res.assertStatus().is(200); 125 * res.assertBody().is(<js>"'foo'"</js>); 126 * 127 * <jc>// Convert the body of the response to a bean.</jc> 128 * myBean = res.getBody().as(MyBean.<jk>class</jk>); 129 * } 130 * </p> 131 * 132 * <p> 133 * The <c>create(Object)</c> method can take in either <c>Class</c> objects or pre-instantiated beans. 134 * The latter is particularly useful for testing Spring beans. 135 * 136 * <p> 137 * The {@link MockRestRequest} object has convenience methods provided to allow you to set any properties 138 * directly on the underlying {@link HttpServletRequest} object. The following example shows how 139 * this can be used to directly set roles on the request object to perform security testing. 140 * 141 * <h5 class='figure'>Example:</h5> 142 * <p class='bcode w800'> 143 * <ja>@Rest</ja>(roleGuard=<js>"ADMIN"</js>) 144 * <jk>public class</jk> A { 145 * <ja>@RestMethod</ja> 146 * <jk>public</jk> String get() { 147 * <jk>return</jk> <js>"OK"</js>; 148 * } 149 * } 150 * 151 * <ja>@Test</ja> 152 * <jk>public void</jk> mytest() <jk>throws</jk> Exception { 153 * MockRestClient a = MockRestClient.<jsm>build</jsm>(A.<jk>class</jk>); 154 * 155 * <jc>// Admin user should get 200, but anyone else should get 403-Unauthorized.</jc> 156 * a.get().roles(<js>"ADMIN"</js>).run().assertStatus().is(200); 157 * a.get().roles(<js>"USER"</js>).run().assertStatus().is(403); 158 * } 159 * </p> 160 * 161 * <p> 162 * Debug mode is provided that will cause your HTTP requests and responses to be sent to the console: 163 * 164 * <h5 class='figure'>Example:</h5> 165 * <p class='bcode w800'> 166 * MockRestClient mr = MockRestClient 167 * .<jsm>create</jsm>(MyRest.<jk>class</jk>) 168 * .debug() 169 * .simpleJson() 170 * .build(); 171 * </p> 172 * 173 * <p> 174 * The class can also be used for testing of {@link Remote @Remote}-annotated interfaces against {@link Rest @Rest}-annotated resources. 175 * 176 * <h5 class='figure'>Example:</h5> 177 * <p class='bpcode w800'> 178 * <jc>// Our remote resource to test.</jc> 179 * <ja>@Remote</ja> 180 * <jk>public interface</jk> MyRemoteInterface { 181 * 182 * <ja>@RemoteMethod</ja>(httpMethod=<js>"GET"</js>, path=<js>"/echoQuery"</js>) 183 * <jk>public int</jk> echoQuery(<ja>@Query</ja>(name=<js>"id"</js>) <jk>int</jk> id); 184 * } 185 * 186 * <jc>// Our mocked-up REST interface to test against.</jc> 187 * <ja>@Rest</ja> 188 * <jk>public class</jk> MyRest { 189 * 190 * <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/echoQuery"</js>) 191 * <jk>public int</jk> echoQuery(<ja>@Query</ja>(<js>"id"</js>) String id) { 192 * <jk>return</jk> id; 193 * } 194 * } 195 * 196 * <ja>@Test</ja> 197 * <jk>public void</jk> testProxy() { 198 * MyRemoteInterface mri = MockRestClient 199 * .create(MyRest.<jk>class</jk>) 200 * .json() 201 * .build() 202 * .getRemote(MyRemoteInterface.<jk>class</jk>); 203 * 204 * <jsm>assertEquals</jsm>(123, mri.echoQuery(123)); 205 * } 206 * </p> 207 * 208 * <ul class='seealso'> 209 * <li class='link'>{@doc juneau-rest-mock} 210 * </ul> 211 */ 212public class MockRestClient extends RestClient implements HttpClientConnection { 213 214 //------------------------------------------------------------------------------------------------------------------- 215 // Configurable properties 216 //------------------------------------------------------------------------------------------------------------------- 217 218 private static final String PREFIX = "RestClient."; 219 220 @SuppressWarnings("javadoc") 221 public static final String 222 MOCKRESTCLIENT_restBean = PREFIX + "restBean.o", 223 MOCKRESTCLIENT_restBeanCtx = PREFIX + "restBeanCtx.o", 224 MOCKRESTCLIENT_servletPath = PREFIX + "servletPath.s", 225 MOCKRESTCLIENT_contextPath = PREFIX + "contextPath.s", 226 MOCKRESTCLIENT_mockHttpClientConnectionManager = PREFIX + "mockHttpClientConnectionManager.o", 227 MOCKRESTCLIENT_pathVars = PREFIX + "pathVars.oms"; 228 229 230 private static Map<Class<?>,RestContext> 231 CONTEXTS_DEBUG = new ConcurrentHashMap<>(), 232 CONTEXTS_NORMAL = new ConcurrentHashMap<>(); 233 234 //------------------------------------------------------------------------------------------------------------------- 235 // Instance properties 236 //------------------------------------------------------------------------------------------------------------------- 237 238 private final RestContext restBeanCtx; 239 private final String contextPath, servletPath; 240 private final Map<String,String> pathVars; 241 242 private final ThreadLocal<HttpRequest> rreq = new ThreadLocal<>(); 243 private final ThreadLocal<MockRestResponse> rres = new ThreadLocal<>(); 244 private final ThreadLocal<MockServletRequest> sreq = new ThreadLocal<>(); 245 private final ThreadLocal<MockServletResponse> sres = new ThreadLocal<>(); 246 247 /** 248 * Constructor. 249 * 250 * @param ps 251 * The REST bean or bean class annotated with {@link Rest @Rest}. 252 * <br>If a class, it must have a no-arg constructor. 253 */ 254 public MockRestClient(PropertyStore ps) { 255 super(preInit(ps)); 256 this.restBeanCtx = getInstanceProperty(MOCKRESTCLIENT_restBeanCtx, RestContext.class, null); 257 this.contextPath = getStringProperty(MOCKRESTCLIENT_contextPath, ""); 258 this.servletPath = getStringProperty(MOCKRESTCLIENT_servletPath, ""); 259 this.pathVars = getMapProperty(MOCKRESTCLIENT_pathVars, String.class); 260 getInstanceProperty(MOCKRESTCLIENT_mockHttpClientConnectionManager, MockHttpClientConnectionManager.class, null).init(this); 261 } 262 263 private static PropertyStore preInit(PropertyStore ps) { 264 try { 265 PropertyStoreBuilder psb = ps.builder(); 266 Object restBean = ps.getInstanceProperty(MOCKRESTCLIENT_restBean, Object.class, null); 267 String contextPath = ps.getProperty(MOCKRESTCLIENT_contextPath, String.class, null); 268 String servletPath = ps.getProperty(MOCKRESTCLIENT_servletPath, String.class, null); 269 String rootUrl = ps.getProperty(RESTCLIENT_rootUri, String.class, "http://localhost"); 270 boolean isDebug = ps.getProperty(CONTEXT_debug, Boolean.class, false); 271 272 Class<?> c = restBean instanceof Class ? (Class<?>)restBean : restBean.getClass(); 273 Map<Class<?>,RestContext> contexts = isDebug ? CONTEXTS_DEBUG : CONTEXTS_NORMAL; 274 if (! contexts.containsKey(c)) { 275 boolean isClass = restBean instanceof Class; 276 Object o = isClass ? ((Class<?>)restBean).newInstance() : restBean; 277 RestContextBuilder rcb = RestContext.create(o); 278 if (isDebug) { 279 rcb.debug(Enablement.TRUE); 280 rcb.callLoggerConfig(RestCallLoggerConfig.DEFAULT_DEBUG); 281 } 282 RestContext rc = rcb.build(); 283 if (o instanceof RestServlet) { 284 RestServlet rs = (RestServlet)o; 285 if (! rs.isInitialized()) 286 rs.setContext(rc); 287 rc = rs.getContext(); 288 } else { 289 rc.postInit(); 290 } 291 rc.postInitChildFirst(); 292 contexts.put(c, rc); 293 } 294 RestContext restBeanCtx = contexts.get(c); 295 psb.set(MOCKRESTCLIENT_restBeanCtx, restBeanCtx); 296 297 if (servletPath == null) 298 servletPath = toValidContextPath(restBeanCtx.getPath()); 299 300 rootUrl = rootUrl + emptyIfNull(contextPath) + emptyIfNull(servletPath); 301 302 psb.set(MOCKRESTCLIENT_servletPath, servletPath); 303 psb.set(RESTCLIENT_rootUri, rootUrl); 304 return psb.build(); 305 } catch (Exception e) { 306 throw new ConfigException(e, "Could not initialize MockRestClient"); 307 } 308 } 309 310 /** 311 * Creates a new {@link RestClientBuilder} configured with the specified REST implementation bean or bean class. 312 * 313 * @param impl 314 * The REST bean or bean class annotated with {@link Rest @Rest}. 315 * <br>If a class, it must have a no-arg constructor. 316 * @return A new builder. 317 */ 318 public static MockRestClientBuilder create(Object impl) { 319 return new MockRestClientBuilder().restBean(impl); 320 } 321 322 /** 323 * Creates a new {@link RestClientBuilder} configured with the specified REST implementation bean or bean class. 324 * 325 * <p> 326 * Same as {@link #create(Object)} but HTTP 400+ codes don't trigger {@link RestCallException RestCallExceptions}. 327 * 328 * @param impl 329 * The REST bean or bean class annotated with {@link Rest @Rest}. 330 * <br>If a class, it must have a no-arg constructor. 331 * @return A new builder. 332 */ 333 public static MockRestClientBuilder createLax(Object impl) { 334 return new MockRestClientBuilder().restBean(impl).ignoreErrors(); 335 } 336 337 /** 338 * Creates a new {@link RestClient} with no registered serializer or parser. 339 * 340 * <p> 341 * Equivalent to calling: 342 * <p class='bcode w800'> 343 * MockRestClient.create(impl).build(); 344 * </p> 345 * 346 * @param impl 347 * The REST bean or bean class annotated with {@link Rest @Rest}. 348 * <br>If a class, it must have a no-arg constructor. 349 * @return A new builder. 350 */ 351 public static MockRestClient build(Object impl) { 352 return create(impl).build(); 353 } 354 355 /** 356 * Creates a new {@link RestClient} with no registered serializer or parser. 357 * 358 * <p> 359 * Same as {@link #build(Object)} but HTTP 400+ codes don't trigger {@link RestCallException RestCallExceptions}. 360 * 361 * <p> 362 * Equivalent to calling: 363 * <p class='bcode w800'> 364 * MockRestClient.create(impl).ignoreErrors().build(); 365 * </p> 366 * 367 * @param impl 368 * The REST bean or bean class annotated with {@link Rest @Rest}. 369 * <br>If a class, it must have a no-arg constructor. 370 * @return A new builder. 371 */ 372 public static MockRestClient buildLax(Object impl) { 373 return create(impl).ignoreErrors().build(); 374 } 375 376 /** 377 * Creates a new {@link RestClient} with JSON marshalling support. 378 * 379 * <p> 380 * Equivalent to calling: 381 * <p class='bcode w800'> 382 * MockRestClient.create(impl).json().build(); 383 * </p> 384 * 385 * @param impl 386 * The REST bean or bean class annotated with {@link Rest @Rest}. 387 * <br>If a class, it must have a no-arg constructor. 388 * @return A new builder. 389 */ 390 public static MockRestClient buildJson(Object impl) { 391 return create(impl).json().build(); 392 } 393 394 /** 395 * Creates a new {@link RestClient} with JSON marshalling support. 396 * 397 * <p> 398 * Same as {@link #buildJson(Object)} but HTTP 400+ codes don't trigger {@link RestCallException RestCallExceptions}. 399 * 400 * <p> 401 * Equivalent to calling: 402 * <p class='bcode w800'> 403 * MockRestClient.create(impl).json().ignoreErrors().build(); 404 * </p> 405 * 406 * @param impl 407 * The REST bean or bean class annotated with {@link Rest @Rest}. 408 * <br>If a class, it must have a no-arg constructor. 409 * @return A new builder. 410 */ 411 public static MockRestClient buildJsonLax(Object impl) { 412 return create(impl).json().ignoreErrors().build(); 413 } 414 415 /** 416 * Creates a new {@link RestClient} with Simplified-JSON marshalling support. 417 * 418 * <p> 419 * Equivalent to calling: 420 * <p class='bcode w800'> 421 * MockRestClient.create(impl).json().build(); 422 * </p> 423 * 424 * @param impl 425 * The REST bean or bean class annotated with {@link Rest @Rest}. 426 * <br>If a class, it must have a no-arg constructor. 427 * @return A new builder. 428 */ 429 public static MockRestClient buildSimpleJson(Object impl) { 430 return create(impl).simpleJson().build(); 431 } 432 433 /** 434 * Creates a new {@link RestClient} with Simplified-JSON marshalling support. 435 * 436 * <p> 437 * Same as {@link #buildSimpleJson(Object)} but HTTP 400+ codes don't trigger {@link RestCallException RestCallExceptions}. 438 * 439 * <p> 440 * Equivalent to calling: 441 * <p class='bcode w800'> 442 * MockRestClient.create(impl).json().ignoreErrors().build(); 443 * </p> 444 * 445 * @param impl 446 * The REST bean or bean class annotated with {@link Rest @Rest}. 447 * <br>If a class, it must have a no-arg constructor. 448 * @return A new builder. 449 */ 450 public static MockRestClient buildSimpleJsonLax(Object impl) { 451 return create(impl).simpleJson().ignoreErrors().build(); 452 } 453 454 //------------------------------------------------------------------------------------------------------------------ 455 // Entry point methods. 456 //------------------------------------------------------------------------------------------------------------------ 457 458 @Override /* RestClient */ 459 public MockRestRequest get(Object url) throws RestCallException { 460 return (MockRestRequest)super.get(url); 461 } 462 463 @Override /* RestClient */ 464 public MockRestRequest get() throws RestCallException { 465 return (MockRestRequest)super.get(); 466 } 467 468 @Override /* RestClient */ 469 public MockRestRequest put(Object url, Object body) throws RestCallException { 470 return (MockRestRequest)super.put(url, body); 471 } 472 473 @Override /* RestClient */ 474 public MockRestRequest put(Object url, String body, String contentType) throws RestCallException { 475 return (MockRestRequest)super.put(url, body, contentType); 476 } 477 478 @Override /* RestClient */ 479 public MockRestRequest put(Object url) throws RestCallException { 480 return (MockRestRequest)super.put(url); 481 } 482 483 @Override /* RestClient */ 484 public MockRestRequest post(Object url, Object body) throws RestCallException { 485 return (MockRestRequest)super.post(url, body); 486 } 487 488 @Override /* RestClient */ 489 public MockRestRequest post(Object url, String body, String contentType) throws RestCallException { 490 return (MockRestRequest)super.post(url, body, contentType); 491 } 492 493 @Override /* RestClient */ 494 public MockRestRequest post(Object url) throws RestCallException { 495 return (MockRestRequest)super.post(url); 496 } 497 498 @Override /* RestClient */ 499 public MockRestRequest delete(Object url) throws RestCallException { 500 return (MockRestRequest)super.delete(url); 501 } 502 503 @Override /* RestClient */ 504 public MockRestRequest options(Object url) throws RestCallException { 505 return (MockRestRequest)super.options(url); 506 } 507 508 @Override /* RestClient */ 509 public MockRestRequest head(Object url) throws RestCallException { 510 return (MockRestRequest)super.head(url); 511 } 512 513 @Override /* RestClient */ 514 public MockRestRequest formPost(Object url, Object body) throws RestCallException { 515 return (MockRestRequest)super.formPost(url, body); 516 } 517 518 @Override /* RestClient */ 519 public MockRestRequest formPost(Object url) throws RestCallException { 520 return (MockRestRequest)super.formPost(url); 521 } 522 523 @Override /* RestClient */ 524 public MockRestRequest formPostPairs(Object url, Object...parameters) throws RestCallException { 525 return (MockRestRequest)super.formPostPairs(url, parameters); 526 } 527 528 @Override /* RestClient */ 529 public MockRestRequest patch(Object url, Object body) throws RestCallException { 530 return (MockRestRequest)super.patch(url, body); 531 } 532 533 @Override /* RestClient */ 534 public MockRestRequest patch(Object url, String body, String contentType) throws RestCallException { 535 return (MockRestRequest)super.patch(url, body, contentType); 536 } 537 538 @Override /* RestClient */ 539 public MockRestRequest patch(Object url) throws RestCallException { 540 return (MockRestRequest)super.patch(url); 541 } 542 543 @Override /* RestClient */ 544 public MockRestRequest callback(String callString) throws RestCallException { 545 return (MockRestRequest)super.callback(callString); 546 } 547 548 @Override /* RestClient */ 549 public MockRestRequest request(String method, Object url, Object body) throws RestCallException { 550 return (MockRestRequest)super.request(method, url, body); 551 } 552 553 @Override /* RestClient */ 554 public MockRestRequest request(String method, Object url) throws RestCallException { 555 return (MockRestRequest)super.request(method, url); 556 } 557 558 @Override /* RestClient */ 559 public MockRestRequest request(String method, Object url, boolean hasBody) throws RestCallException { 560 return (MockRestRequest)super.request(method, url, hasBody); 561 } 562 563 //------------------------------------------------------------------------------------------------------------------ 564 // Getters and setters. 565 //------------------------------------------------------------------------------------------------------------------ 566 567 /** 568 * Returns the current client-side REST request. 569 * 570 * <p> 571 * Note that this uses a {@link ThreadLocal} object for storage and so will not work on requests executed in 572 * separate threads such as when using {@link Future Futures}. 573 * 574 * @return The current client-side REST request, or <jk>null</jk> if not set. 575 */ 576 public HttpRequest getCurrentClientRequest() { 577 return rreq.get(); 578 } 579 580 /** 581 * Returns the current client-side REST response. 582 * 583 * <p> 584 * Note that this uses a {@link ThreadLocal} object for storage and so will not work on requests executed in 585 * separate threads such as when using {@link Future Futures}. 586 * 587 * @return The current client-side REST response, or <jk>null</jk> if not set. 588 */ 589 public MockRestResponse getCurrentClientResponse() { 590 return rres.get(); 591 } 592 593 /** 594 * Returns the current server-side REST request. 595 * 596 * <p> 597 * Note that this uses a {@link ThreadLocal} object for storage and so will not work on requests executed in 598 * separate threads such as when using {@link Future Futures}. 599 * 600 * @return The current server-side REST request, or <jk>null</jk> if not set. 601 */ 602 public MockServletRequest getCurrentServerRequest() { 603 return sreq.get(); 604 } 605 606 /** 607 * Returns the current server-side REST response. 608 * 609 * <p> 610 * Note that this uses a {@link ThreadLocal} object for storage and so will not work on requests executed in 611 * separate threads such as when using {@link Future Futures}. 612 * 613 * @return The current server-side REST response, or <jk>null</jk> if not set. 614 */ 615 public MockServletResponse getCurrentServerResponse() { 616 return sres.get(); 617 } 618 619 MockRestClient currentResponse(MockRestResponse value) { 620 rres.set(value); 621 return this; 622 } 623 624 //------------------------------------------------------------------------------------------------------------------ 625 // RestClient methods. 626 //------------------------------------------------------------------------------------------------------------------ 627 628 @Override /* RestClient */ 629 protected MockRestRequest createRequest(URI uri, String method, boolean hasBody) throws RestCallException { 630 return new MockRestRequest(this, uri, method, hasBody); 631 } 632 633 @Override /* RestClient */ 634 protected MockRestResponse createResponse(RestRequest req, HttpResponse httpResponse, Parser parser) throws RestCallException { 635 return new MockRestResponse(this, req, httpResponse, parser); 636 } 637 638 //------------------------------------------------------------------------------------------------------------------ 639 // HttpClientConnection methods. 640 //------------------------------------------------------------------------------------------------------------------ 641 642 @Override /* HttpClientConnection */ 643 public void close() throws IOException { 644 // Don't call super.close() because it will close the client. 645 rreq.remove(); 646 rres.remove(); 647 sreq.remove(); 648 sres.remove(); 649 } 650 651 @Override /* HttpClientConnection */ 652 public boolean isOpen() { 653 return true; 654 } 655 656 @Override /* HttpClientConnection */ 657 public boolean isStale() { 658 return false; 659 } 660 661 @Override /* HttpClientConnection */ 662 public void setSocketTimeout(int timeout) {} 663 664 @Override /* HttpClientConnection */ 665 public int getSocketTimeout() { 666 return Integer.MAX_VALUE; 667 } 668 669 @Override /* HttpClientConnection */ 670 public void shutdown() throws IOException {} 671 672 @Override /* HttpClientConnection */ 673 public HttpConnectionMetrics getMetrics() { 674 return null; 675 } 676 677 @Override /* HttpClientConnection */ 678 public boolean isResponseAvailable(int timeout) throws IOException { 679 return true; 680 } 681 682 @Override /* HttpClientConnection */ 683 public void sendRequestHeader(HttpRequest request) throws HttpException, IOException { 684 try { 685 RequestLine rl = request.getRequestLine(); 686 String path = rl.getUri(); 687 String target = findTarget(request); 688 689 HttpRequest req = findRestRequest(request); 690 rreq.set(req); 691 rres.remove(); 692 sreq.remove(); 693 sres.remove(); 694 695 path = target + path; 696 697 MockPathResolver pr = new MockPathResolver(target, contextPath, servletPath, path, null); 698 if (pr.getError() != null) 699 throw new RuntimeException(pr.getError()); 700 701 MockServletRequest r = MockServletRequest 702 .create(request.getRequestLine().getMethod(), pr.getURI()) 703 .contextPath(pr.getContextPath()) 704 .servletPath(pr.getServletPath()) 705 .pathVars(pathVars) 706 .debug(isDebug()); 707 708 for (Header h : request.getAllHeaders()) 709 r.header(h.getName(), h.getValue()); 710 711 sreq.set(r); 712 sreq.get().applyOverrides(req); 713 } catch (Exception e) { 714 throw new HttpException(e.getMessage(), e); 715 } 716 } 717 718 /** 719 * Attempts to unwrap the request to find the underlying RestRequest object. 720 * Returns the same object if one of the low-level client methods are used (e.g. execute(HttpUriRequest)). 721 */ 722 private HttpRequest findRestRequest(HttpRequest req) { 723 if (req instanceof RestRequestCreated) 724 return ((RestRequestCreated)req).getRestRequest(); 725 if (req instanceof HttpRequestWrapper) 726 return findRestRequest(((HttpRequestWrapper) req).getOriginal()); 727 return req; 728 } 729 730 private String findTarget(HttpRequest req) { 731 if (req instanceof HttpRequestWrapper) { 732 HttpHost httpHost = ((HttpRequestWrapper)req).getTarget(); 733 if (httpHost != null) 734 return httpHost.toURI(); 735 } 736 return "http://localhost"; 737 } 738 739 @Override /* HttpClientConnection */ 740 public void sendRequestEntity(HttpEntityEnclosingRequest request) throws HttpException, IOException { 741 byte[] body = new byte[0]; 742 HttpEntity entity = request.getEntity(); 743 if (entity != null) { 744 long length = entity.getContentLength(); 745 if (length < 0) 746 length = 1024; 747 ByteArrayOutputStream baos = new ByteArrayOutputStream((int)Math.min(length, 1024)); 748 entity.writeTo(baos); 749 body = baos.toByteArray(); 750 } 751 sreq.get().body(body); 752 } 753 754 @Override /* HttpClientConnection */ 755 public HttpResponse receiveResponseHeader() throws HttpException, IOException { 756 try { 757 MockServletResponse res = MockServletResponse.create(); 758 restBeanCtx.execute(sreq.get(), res); 759 760 // If the status isn't set, something's broken. 761 if (res.getStatus() == 0) 762 throw new RuntimeException("Response status was 0."); 763 764 // A bug in HttpClient causes an infinite loop if the response is less than 200. 765 // As a workaround, just add 1000 to the status code (which is better than an infinite loop). 766 if (res.getStatus() < 200) 767 res.setStatus(1000 + res.getStatus()); 768 769 sres.set(res); 770 771 HttpResponse response = new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, res.getStatus(), res.getMessage())); 772 for (Map.Entry<String,String[]> e : res.getHeaders().entrySet()) 773 for (String hv : e.getValue()) 774 response.addHeader(e.getKey(), hv); 775 776 return response; 777 } catch (Exception e) { 778 throw new HttpException(emptyIfNull(e.getMessage()), e); 779 } 780 } 781 782 @Override /* HttpClientConnection */ 783 public void receiveResponseEntity(HttpResponse response) throws HttpException, IOException { 784 InputStream is = new ByteArrayInputStream(sres.get().getBody()); 785 Header contentEncoding = response.getLastHeader("Content-Encoding"); 786 if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) 787 is = new GZIPInputStream(is); 788 response.setEntity(new InputStreamEntity(is)); 789 } 790 791 @Override /* HttpClientConnection */ 792 public void flush() throws IOException {} 793}