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}