blob: 634c1eaa3e56b2f5af000950f02aee1c9d271382 [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.rest.mock2;
import static org.apache.juneau.internal.StringUtils.*;
import static org.apache.juneau.rest.util.RestUtils.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.zip.*;
import javax.servlet.http.*;
import org.apache.http.*;
import org.apache.http.client.methods.*;
import org.apache.http.entity.*;
import org.apache.http.message.*;
import org.apache.juneau.*;
import org.apache.juneau.http.remote.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.client2.*;
import org.apache.juneau.rest.client2.RestRequest;
/**
* Mocked {@link RestClient}.
*
* <p>
* This class is used for performing serverless unit testing of {@link Rest @Rest}-annotated and {@link Remote @Remote}-annotated classes.
*
* <p>
* The class itself extends from {@link RestClient} providing it with the rich feature set of that API and combines
* it with the Apache HttpClient {@link HttpClientConnection} interface for processing requests.
* The class converts {@link HttpRequest} objects to instances of {@link MockServletRequest} and {@link MockServletResponse} which are passed directly
* to the call handler on the resource class {@link RestContext#execute(HttpServletRequest,HttpServletResponse)}.
* In effect, you're fully testing your REST API as if it were running in a live servlet container, yet not
* actually having to run in a servlet container.
* All aspects of the client and server side code are tested, yet no servlet container is required. The actual
* over-the-wire transmission is the only aspect being bypassed.
*
* <p>
* 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:
*
* <h5 class='figure'>Example:</h5>
* <p class='bcode w800'>
* <jk>public class</jk> MockTest {
*
* <jc>// A simple bean with one field.</jc>
* <jk>public static class</jk> MyBean {
* <jk>public int</jk> <jf>foo</jf> = 1;
* }
*
* <jc>// Our REST resource to test.</jc>
* <jc>// Simply echos the response.</jc>
* <ja>@Rest</ja>(
* serializers=SimpleJsonSerializer.<jk>class</jk>,
* parsers=JsonParser.<jk>class</jk>
* )
* <jk>public static class</jk> EchoRest {
*
* <ja>@RestMethod</ja>(
* name=<jsf>PUT</jsf>,
* path=<js>"/echo"</js>
* )
* <jk>public</jk> MyBean echo(<ja>@Body</ja> MyBean bean) {
* <jk>return</jk> bean;
* }
* }
*
* <jc>// Our JUnit test.</jc>
* <ja>@Test</ja>
* <jk>public void</jk> testEcho() <jk>throws</jk> Exception {
*
* MyBean myBean = <jk>new</jk> MyBean();
*
* <jc>// Do a round-trip on the bean through the REST interface</jc>
* myBean = MockRestClient
* .<jsm>create</jsm>(EchoRest.<jk>class</jk>)
* .simpleJson()
* .build()
* .put(<js>"/echo"</js>, myBean)
* .run()
* .assertStatus().is(200)
* .assertBody().is(<js>"{foo:1}"</js>)
* .getBody().as(MyBean.<jk>class</jk>);
*
* <jsm>assertEquals</jsm>(1, myBean.<jf>foo</jf>);
* }
* }
* </p>
* <p>
* Breaking apart the fluent method call above will help you understand how this works.
*
* <p class='bcode w800'>
* <ja>@Test</ja>
* <jk>public void</jk> testEcho() <jk>throws</jk> Exception {
*
* <jc>// Instantiate our mock client.</jc>
* MockRestClient client = MockRestClient
* .<jsm>create</jsm>(EchoRest.<jk>class</jk>)
* .simpleJson()
* .build();
*
* <jc>// Create a request.</jc>
* RestRequest req = client.put(<js>"/echo"</js>, myBean);
*
* <jc>// Execute it (by calling RestCallHandler.service(...) and then returning the response object).</jc>
* RestResponse res = req.run();
*
* <jc>// Run assertion tests on the results.</jc>
* res.assertStatus().is(200);
* res.assertBody().is(<js>"'foo'"</js>);
*
* <jc>// Convert the body of the response to a bean.</jc>
* myBean = res.getBody().as(MyBean.<jk>class</jk>);
* }
* </p>
*
* <p>
* The <c>create(Object)</c> method can take in either <c>Class</c> objects or pre-instantiated beans.
* The latter is particularly useful for testing Spring beans.
*
* <p>
* The {@link MockRestRequest} object has convenience methods provided to allow you to set any properties
* directly on the underlying {@link HttpServletRequest} object. The following example shows how
* this can be used to directly set roles on the request object to perform security testing.
*
* <h5 class='figure'>Example:</h5>
* <p class='bcode w800'>
* <ja>@Rest</ja>(roleGuard=<js>"ADMIN"</js>)
* <jk>public class</jk> A {
* <ja>@RestMethod</ja>
* <jk>public</jk> String get() {
* <jk>return</jk> <js>"OK"</js>;
* }
* }
*
* <ja>@Test</ja>
* <jk>public void</jk> mytest() <jk>throws</jk> Exception {
* MockRestClient a = MockRestClient.<jsm>build</jsm>(A.<jk>class</jk>);
*
* <jc>// Admin user should get 200, but anyone else should get 403-Unauthorized.</jc>
* a.get().roles(<js>"ADMIN"</js>).run().assertStatus().is(200);
* a.get().roles(<js>"USER"</js>).run().assertStatus().is(403);
* }
* </p>
*
* <p>
* Debug mode is provided that will cause your HTTP requests and responses to be sent to the console:
*
* <h5 class='figure'>Example:</h5>
* <p class='bcode w800'>
* MockRestClient mr = MockRestClient
* .<jsm>create</jsm>(MyRest.<jk>class</jk>)
* .debug()
* .simpleJson()
* .build();
* </p>
*
* <p>
* The class can also be used for testing of {@link Remote @Remote}-annotated interfaces against {@link Rest @Rest}-annotated resources.
*
* <h5 class='figure'>Example:</h5>
* <p class='bpcode w800'>
* <jc>// Our remote resource to test.</jc>
* <ja>@Remote</ja>
* <jk>public interface</jk> MyRemoteInterface {
*
* <ja>@RemoteMethod</ja>(httpMethod=<js>"GET"</js>, path=<js>"/echoQuery"</js>)
* <jk>public int</jk> echoQuery(<ja>@Query</ja>(name=<js>"id"</js>) <jk>int</jk> id);
* }
*
* <jc>// Our mocked-up REST interface to test against.</jc>
* <ja>@Rest</ja>
* <jk>public class</jk> MyRest {
*
* <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/echoQuery"</js>)
* <jk>public int</jk> echoQuery(<ja>@Query</ja>(<js>"id"</js>) String id) {
* <jk>return</jk> id;
* }
* }
*
* <ja>@Test</ja>
* <jk>public void</jk> testProxy() {
* MyRemoteInterface mri = MockRestClient
* .create(MyRest.<jk>class</jk>)
* .json()
* .build()
* .getRemote(MyRemoteInterface.<jk>class</jk>);
*
* <jsm>assertEquals</jsm>(123, mri.echoQuery(123));
* }
* </p>
*
* <ul class='seealso'>
* <li class='link'>{@doc juneau-rest-mock}
* </ul>
*/
public class MockRestClient extends RestClient implements HttpClientConnection {
//-------------------------------------------------------------------------------------------------------------------
// Configurable properties
//-------------------------------------------------------------------------------------------------------------------
private static final String PREFIX = "RestClient.";
@SuppressWarnings("javadoc")
public static final String
MOCKRESTCLIENT_restBean = PREFIX + "restBean.o",
MOCKRESTCLIENT_restBeanCtx = PREFIX + "restBeanCtx.o",
MOCKRESTCLIENT_servletPath = PREFIX + "servletPath.s",
MOCKRESTCLIENT_contextPath = PREFIX + "contextPath.s",
MOCKRESTCLIENT_mockHttpClientConnectionManager = PREFIX + "mockHttpClientConnectionManager.o";
private static Map<Class<?>,RestContext>
CONTEXTS_DEBUG = new ConcurrentHashMap<>(),
CONTEXTS_NORMAL = new ConcurrentHashMap<>();
//-------------------------------------------------------------------------------------------------------------------
// Instance properties
//-------------------------------------------------------------------------------------------------------------------
private final RestContext restBeanCtx;
private final String contextPath, servletPath;
private final ThreadLocal<HttpRequest> rreq = new ThreadLocal<>();
private final ThreadLocal<MockRestResponse> rres = new ThreadLocal<>();
private final ThreadLocal<MockServletRequest> sreq = new ThreadLocal<>();
private final ThreadLocal<MockServletResponse> sres = new ThreadLocal<>();
/**
* Constructor.
*
* @param ps
* The REST bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
*/
public MockRestClient(PropertyStore ps) {
super(preInit(ps));
this.restBeanCtx = getInstanceProperty(MOCKRESTCLIENT_restBeanCtx, RestContext.class, null);
this.contextPath = getStringProperty(MOCKRESTCLIENT_contextPath, "");
this.servletPath = getStringProperty(MOCKRESTCLIENT_servletPath, "");
getInstanceProperty(MOCKRESTCLIENT_mockHttpClientConnectionManager, MockHttpClientConnectionManager.class, null).init(this);
}
private static PropertyStore preInit(PropertyStore ps) {
try {
PropertyStoreBuilder psb = ps.builder();
Object restBean = ps.getInstanceProperty(MOCKRESTCLIENT_restBean, Object.class, null);
String contextPath = ps.getProperty(MOCKRESTCLIENT_contextPath, String.class, null);
String servletPath = ps.getProperty(MOCKRESTCLIENT_servletPath, String.class, null);
String rootUrl = ps.getProperty(RESTCLIENT_rootUri, String.class, "http://localhost");
boolean isDebug = ps.getProperty(CONTEXT_debug, Boolean.class, false);
Class<?> c = restBean instanceof Class ? (Class<?>)restBean : restBean.getClass();
Map<Class<?>,RestContext> contexts = isDebug ? CONTEXTS_DEBUG : CONTEXTS_NORMAL;
if (! contexts.containsKey(c)) {
boolean isClass = restBean instanceof Class;
Object o = isClass ? ((Class<?>)restBean).newInstance() : restBean;
RestContextBuilder rcb = RestContext.create(o);
if (isDebug) {
rcb.debug(Enablement.TRUE);
rcb.callLoggerConfig(RestCallLoggerConfig.DEFAULT_DEBUG);
}
RestContext rc = rcb.build();
if (o instanceof RestServlet) {
RestServlet rs = (RestServlet)o;
if (! rs.isInitialized())
rs.setContext(rc);
rc = rs.getContext();
} else {
rc.postInit();
}
rc.postInitChildFirst();
contexts.put(c, rc);
}
RestContext restBeanCtx = contexts.get(c);
psb.set(MOCKRESTCLIENT_restBeanCtx, restBeanCtx);
if (servletPath == null)
servletPath = toValidContextPath(restBeanCtx.getPath());
rootUrl = rootUrl + emptyIfNull(contextPath) + emptyIfNull(servletPath);
psb.set(MOCKRESTCLIENT_servletPath, servletPath);
psb.set(RESTCLIENT_rootUri, rootUrl);
return psb.build();
} catch (Exception e) {
throw new ConfigException(e, "Could not initialize MockRestClient");
}
}
/**
* Creates a new {@link RestClientBuilder} configured with the specified REST implementation bean or bean class.
*
* @param impl
* The REST bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @return A new builder.
*/
public static MockRestClientBuilder create(Object impl) {
return new MockRestClientBuilder().restBean(impl);
}
/**
* Creates a new {@link RestClientBuilder} configured with the specified REST implementation bean or bean class.
*
* <p>
* Same as {@link #create(Object)} but HTTP 400+ codes don't trigger {@link RestCallException RestCallExceptions}.
*
* @param impl
* The REST bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @return A new builder.
*/
public static MockRestClientBuilder createLax(Object impl) {
return new MockRestClientBuilder().restBean(impl).ignoreErrors();
}
/**
* Creates a new {@link RestClient} with no registered serializer or parser.
*
* <p>
* Equivalent to calling:
* <p class='bcode w800'>
* MockRestClient.create(impl).build();
* </p>
*
* @param impl
* The REST bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @return A new builder.
*/
public static MockRestClient build(Object impl) {
return create(impl).build();
}
/**
* Creates a new {@link RestClient} with no registered serializer or parser.
*
* <p>
* Same as {@link #build(Object)} but HTTP 400+ codes don't trigger {@link RestCallException RestCallExceptions}.
*
* <p>
* Equivalent to calling:
* <p class='bcode w800'>
* MockRestClient.create(impl).ignoreErrors().build();
* </p>
*
* @param impl
* The REST bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @return A new builder.
*/
public static MockRestClient buildLax(Object impl) {
return create(impl).ignoreErrors().build();
}
/**
* Creates a new {@link RestClient} with JSON marshalling support.
*
* <p>
* Equivalent to calling:
* <p class='bcode w800'>
* MockRestClient.create(impl).json().build();
* </p>
*
* @param impl
* The REST bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @return A new builder.
*/
public static MockRestClient buildJson(Object impl) {
return create(impl).json().build();
}
/**
* Creates a new {@link RestClient} with JSON marshalling support.
*
* <p>
* Same as {@link #buildJson(Object)} but HTTP 400+ codes don't trigger {@link RestCallException RestCallExceptions}.
*
* <p>
* Equivalent to calling:
* <p class='bcode w800'>
* MockRestClient.create(impl).json().ignoreErrors().build();
* </p>
*
* @param impl
* The REST bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @return A new builder.
*/
public static MockRestClient buildJsonLax(Object impl) {
return create(impl).json().ignoreErrors().build();
}
/**
* Creates a new {@link RestClient} with Simplified-JSON marshalling support.
*
* <p>
* Equivalent to calling:
* <p class='bcode w800'>
* MockRestClient.create(impl).json().build();
* </p>
*
* @param impl
* The REST bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @return A new builder.
*/
public static MockRestClient buildSimpleJson(Object impl) {
return create(impl).simpleJson().build();
}
/**
* Creates a new {@link RestClient} with Simplified-JSON marshalling support.
*
* <p>
* Same as {@link #buildSimpleJson(Object)} but HTTP 400+ codes don't trigger {@link RestCallException RestCallExceptions}.
*
* <p>
* Equivalent to calling:
* <p class='bcode w800'>
* MockRestClient.create(impl).json().ignoreErrors().build();
* </p>
*
* @param impl
* The REST bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @return A new builder.
*/
public static MockRestClient buildSimpleJsonLax(Object impl) {
return create(impl).simpleJson().ignoreErrors().build();
}
//------------------------------------------------------------------------------------------------------------------
// Entry point methods.
//------------------------------------------------------------------------------------------------------------------
@Override /* RestClient */
public MockRestRequest get(Object url) throws RestCallException {
return (MockRestRequest)super.get(url);
}
@Override /* RestClient */
public MockRestRequest get() throws RestCallException {
return (MockRestRequest)super.get();
}
@Override /* RestClient */
public MockRestRequest put(Object url, Object body) throws RestCallException {
return (MockRestRequest)super.put(url, body);
}
@Override /* RestClient */
public MockRestRequest put(Object url, String body, String contentType) throws RestCallException {
return (MockRestRequest)super.put(url, body, contentType);
}
@Override /* RestClient */
public MockRestRequest put(Object url) throws RestCallException {
return (MockRestRequest)super.put(url);
}
@Override /* RestClient */
public MockRestRequest post(Object url, Object body) throws RestCallException {
return (MockRestRequest)super.post(url, body);
}
@Override /* RestClient */
public MockRestRequest post(Object url, String body, String contentType) throws RestCallException {
return (MockRestRequest)super.post(url, body, contentType);
}
@Override /* RestClient */
public MockRestRequest post(Object url) throws RestCallException {
return (MockRestRequest)super.post(url);
}
@Override /* RestClient */
public MockRestRequest delete(Object url) throws RestCallException {
return (MockRestRequest)super.delete(url);
}
@Override /* RestClient */
public MockRestRequest options(Object url) throws RestCallException {
return (MockRestRequest)super.options(url);
}
@Override /* RestClient */
public MockRestRequest head(Object url) throws RestCallException {
return (MockRestRequest)super.head(url);
}
@Override /* RestClient */
public MockRestRequest formPost(Object url, Object body) throws RestCallException {
return (MockRestRequest)super.formPost(url, body);
}
@Override /* RestClient */
public MockRestRequest formPost(Object url) throws RestCallException {
return (MockRestRequest)super.formPost(url);
}
@Override /* RestClient */
public MockRestRequest formPostPairs(Object url, Object...parameters) throws RestCallException {
return (MockRestRequest)super.formPostPairs(url, parameters);
}
@Override /* RestClient */
public MockRestRequest patch(Object url, Object body) throws RestCallException {
return (MockRestRequest)super.patch(url, body);
}
@Override /* RestClient */
public MockRestRequest patch(Object url, String body, String contentType) throws RestCallException {
return (MockRestRequest)super.patch(url, body, contentType);
}
@Override /* RestClient */
public MockRestRequest patch(Object url) throws RestCallException {
return (MockRestRequest)super.patch(url);
}
@Override /* RestClient */
public MockRestRequest callback(String callString) throws RestCallException {
return (MockRestRequest)super.callback(callString);
}
@Override /* RestClient */
public MockRestRequest request(String method, Object url, Object body) throws RestCallException {
return (MockRestRequest)super.request(method, url, body);
}
@Override /* RestClient */
public MockRestRequest request(String method, Object url) throws RestCallException {
return (MockRestRequest)super.request(method, url);
}
@Override /* RestClient */
public MockRestRequest request(String method, Object url, boolean hasBody) throws RestCallException {
return (MockRestRequest)super.request(method, url, hasBody);
}
//------------------------------------------------------------------------------------------------------------------
// Getters and setters.
//------------------------------------------------------------------------------------------------------------------
/**
* Returns the current client-side REST request.
*
* <p>
* Note that this uses a {@link ThreadLocal} object for storage and so will not work on requests executed in
* separate threads such as when using {@link Future Futures}.
*
* @return The current client-side REST request, or <jk>null</jk> if not set.
*/
public HttpRequest getCurrentClientRequest() {
return rreq.get();
}
/**
* Returns the current client-side REST response.
*
* <p>
* Note that this uses a {@link ThreadLocal} object for storage and so will not work on requests executed in
* separate threads such as when using {@link Future Futures}.
*
* @return The current client-side REST response, or <jk>null</jk> if not set.
*/
public MockRestResponse getCurrentClientResponse() {
return rres.get();
}
/**
* Returns the current server-side REST request.
*
* <p>
* Note that this uses a {@link ThreadLocal} object for storage and so will not work on requests executed in
* separate threads such as when using {@link Future Futures}.
*
* @return The current server-side REST request, or <jk>null</jk> if not set.
*/
public MockServletRequest getCurrentServerRequest() {
return sreq.get();
}
/**
* Returns the current server-side REST response.
*
* <p>
* Note that this uses a {@link ThreadLocal} object for storage and so will not work on requests executed in
* separate threads such as when using {@link Future Futures}.
*
* @return The current server-side REST response, or <jk>null</jk> if not set.
*/
public MockServletResponse getCurrentServerResponse() {
return sres.get();
}
MockRestClient currentResponse(MockRestResponse value) {
rres.set(value);
return this;
}
//------------------------------------------------------------------------------------------------------------------
// RestClient methods.
//------------------------------------------------------------------------------------------------------------------
@Override /* RestClient */
protected MockRestRequest createRequest(URI uri, String method, boolean hasBody) throws RestCallException {
return new MockRestRequest(this, uri, method, hasBody);
}
@Override /* RestClient */
protected MockRestResponse createResponse(RestRequest req, HttpResponse httpResponse, Parser parser) throws RestCallException {
return new MockRestResponse(this, req, httpResponse, parser);
}
//------------------------------------------------------------------------------------------------------------------
// HttpClientConnection methods.
//------------------------------------------------------------------------------------------------------------------
@Override /* HttpClientConnection */
public void close() throws IOException {
// Don't call super.close() because it will close the client.
rreq.remove();
rres.remove();
sreq.remove();
sres.remove();
}
@Override /* HttpClientConnection */
public boolean isOpen() {
return true;
}
@Override /* HttpClientConnection */
public boolean isStale() {
return false;
}
@Override /* HttpClientConnection */
public void setSocketTimeout(int timeout) {}
@Override /* HttpClientConnection */
public int getSocketTimeout() {
return Integer.MAX_VALUE;
}
@Override /* HttpClientConnection */
public void shutdown() throws IOException {}
@Override /* HttpClientConnection */
public HttpConnectionMetrics getMetrics() {
return null;
}
@Override /* HttpClientConnection */
public boolean isResponseAvailable(int timeout) throws IOException {
return true;
}
@Override /* HttpClientConnection */
public void sendRequestHeader(HttpRequest request) throws HttpException, IOException {
try {
RequestLine rl = request.getRequestLine();
String path = rl.getUri();
String target = findTarget(request);
HttpRequest req = findRestRequest(request);
rreq.set(req);
rres.remove();
sreq.remove();
sres.remove();
path = target + path;
MockPathResolver pr = new MockPathResolver(target, contextPath, servletPath, path, null);
if (pr.getError() != null)
throw new RuntimeException(pr.getError());
MockServletRequest r = MockServletRequest
.create(request.getRequestLine().getMethod(), pr.getURI())
.contextPath(pr.getContextPath())
.servletPath(pr.getServletPath())
.debug(isDebug());
for (Header h : request.getAllHeaders())
r.header(h.getName(), h.getValue());
sreq.set(r);
sreq.get().applyOverrides(req);
} catch (Exception e) {
throw new HttpException(e.getMessage(), e);
}
}
/**
* Attempts to unwrap the request to find the underlying RestRequest object.
* Returns the same object if one of the low-level client methods are used (e.g. execute(HttpUriRequest)).
*/
private HttpRequest findRestRequest(HttpRequest req) {
if (req instanceof RestRequestCreated)
return ((RestRequestCreated)req).getRestRequest();
if (req instanceof HttpRequestWrapper)
return findRestRequest(((HttpRequestWrapper) req).getOriginal());
return req;
}
private String findTarget(HttpRequest req) {
if (req instanceof HttpRequestWrapper) {
HttpHost httpHost = ((HttpRequestWrapper)req).getTarget();
if (httpHost != null)
return httpHost.toURI();
}
return "http://localhost";
}
@Override /* HttpClientConnection */
public void sendRequestEntity(HttpEntityEnclosingRequest request) throws HttpException, IOException {
byte[] body = new byte[0];
HttpEntity entity = request.getEntity();
if (entity != null) {
long length = entity.getContentLength();
if (length < 0)
length = 1024;
ByteArrayOutputStream baos = new ByteArrayOutputStream((int)Math.min(length, 1024));
entity.writeTo(baos);
body = baos.toByteArray();
}
sreq.get().body(body);
}
@Override /* HttpClientConnection */
public HttpResponse receiveResponseHeader() throws HttpException, IOException {
try {
MockServletResponse res = MockServletResponse.create();
restBeanCtx.execute(sreq.get(), res);
// If the status isn't set, something's broken.
if (res.getStatus() == 0)
throw new RuntimeException("Response status was 0.");
// A bug in HttpClient causes an infinite loop if the response is less than 200.
// As a workaround, just add 1000 to the status code (which is better than an infinite loop).
if (res.getStatus() < 200)
res.setStatus(1000 + res.getStatus());
sres.set(res);
HttpResponse response = new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, res.getStatus(), res.getMessage()));
for (Map.Entry<String,String[]> e : res.getHeaders().entrySet())
for (String hv : e.getValue())
response.addHeader(e.getKey(), hv);
return response;
} catch (Exception e) {
throw new HttpException(emptyIfNull(e.getMessage()), e);
}
}
@Override /* HttpClientConnection */
public void receiveResponseEntity(HttpResponse response) throws HttpException, IOException {
InputStream is = new ByteArrayInputStream(sres.get().getBody());
Header contentEncoding = response.getLastHeader("Content-Encoding");
if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip"))
is = new GZIPInputStream(is);
response.setEntity(new InputStreamEntity(is));
}
@Override /* HttpClientConnection */
public void flush() throws IOException {}
}