Our application makes various usages of apache HttpAsyncClient:
CloseableHttpAsyncClient client= ...
HttpGet get = new HttpGet(...);
Future<HttpResponse> f = client.execute(get,null);
HttpResponse resp=f.get()
I'm looking for some hook to capture the response just before it's passed on to the business code that invoked 'f.get()' . Inside this hook, I'll perform auditing and security sanitation. BTW Responses are short texts, so there's no problem with buffering.
Would anyone please happen to know of such hooks?
I tried HttpRequestInterceptor, but it seems to work only for synchronous client:
// hook to audit & sanitize *synchronous* client response:
HttpClients.custom().addInterceptorLast(new HttpRequestInterceptor(){
public void process(HttpRequest req, HttpContext ctx) {
HttpEntityEnclosingRequest enclosing=(HttpEntityEnclosingRequest)req;
String body=EntityUtils.toString(enclosing.getEntity());
// ... audit 'body'
// ... sanitize 'body'
enclosing.setEntity(new StringEntity(sanitizedBody))
Unfortunately it doesn't work for async client - I suspect the interceptor runs before response is ready; I'm looking for a hook that runs when async response is ready.
Thanks
Consider using a custom HttpAsyncResponseConsumer. This should give you a complete control over the response message processing.
CloseableHttpAsyncClient client = HttpAsyncClients.createDefault();
HttpAsyncResponseConsumer responseConsumer = new BasicAsyncResponseConsumer() {
#Override
protected void onResponseReceived(final HttpResponse response) throws IOException {
super.onResponseReceived(response);
}
#Override
protected void onEntityEnclosed(final HttpEntity entity, final ContentType contentType) throws IOException {
super.onEntityEnclosed(entity, contentType);
}
#Override
protected void onContentReceived(final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
super.onContentReceived(decoder, ioctrl);
}
#Override
protected HttpResponse buildResult(HttpContext context) {
return super.buildResult(context);
}
#Override
protected void releaseResources() {
super.releaseResources();
}
};
client.execute(HttpAsyncMethods.createGet("http://target/"), consumer, null);
PS: one can have access to message content stream from inside a protocol interceptor with blocking HttpClient but not with HttpAsyncClient
Related
Suffered a lot in finding how to mock http response . Mocking http request for the same library was easy . Thought to create a thread here , to save your time should you need it .
Requirement ->
Wanted to mock a HttpResponse that is returned when any HttpRequest object is executed . (Note - this is specifically for google client api library)
//creating mockContent for httpRequest
MockHttpContent mockHttpContent = new MockHttpContent();
String content = new String("requestBody");
mockHttpContent.setContent(str.getBytes());
//mocking httpResponse and linking to httpRequest's execution
HttpTransport transport =
new MockHttpTransport() {
#Override
public LowLevelHttpRequest buildRequest(String method, String url) throws IOException {
return new MockLowLevelHttpRequest() {
#Override
public LowLevelHttpResponse execute() throws IOException {
MockLowLevelHttpResponse result = new MockLowLevelHttpResponse();
result.setContent("responseBody");
result.setContentEncoding("UTF-8");//this is very important
result.setHeaderNames(List.of("header1","header2"));
result.setHeaderValues(List.of("header1","header2"));
return result;
}
};
}
};
HttpRequest httpRequest = transport.createRequestFactory().buildPostRequest(HttpTesting.SIMPLE_GENERIC_URL,mockHttpContent);
//getting httpResponse from httpRequest
httpResponse = httpRequest.execute();
//condition to verify the content (body) of the response
assertEquals("responseBody",IOUtils.toString(httpResponse.getContent()));
I want to a upload file on my server and I've decided to try OKHTTP instead of my current method which is based on android own HTTP implementation and AsyncTask.
Anyway, I used OKHTTP and its asynchronous implementation (from its own recipes) but it returns an empty message (the request code is ok, the message is empty) in both GET and POST methods.
Did I implement it wrong or is there anything else remained that I did not considered? In the meantime, I couldn't find a similar case except this which says used AsyncTask.
Here's the code:
Request request;
Response response;
private final OkHttpClient client = new OkHttpClient();
private static final String postman_url = "https://postman-echo.com/get?foo1=bar1&foo2=bar2";
String message_body;
public void Get_Synchronous() throws IOException
{
request = new Request.Builder()
.url(postman_url)
.build();
Call call = client.newCall(request);
response = call.execute();
message_body = response.toString();
//assertThat(response.code(), equalTo(200));
}
public void Get_Asynchronous()
{
request = new Request.Builder()
.url(postman_url)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
public void onResponse(Call call, Response response)
throws IOException
{
message_body = response.toString();
}
public void onFailure(Call call, IOException e)
{
}
});
}
Edit:
I catch the log on response:
onResponse: Response{protocol=h2, code=200, message=, url=https://postman-echo.com/get?foo1=bar1&foo2=bar2}
OK, for anyone who wants to receive an string from a call, response and response.messgage() don't provide that. To catch the response from your provider, you just need to call response.body().string() inside onResponse which returns the message inside your request.
But after all, Retrofit is a better choice if you want to receive a JSON file using
.addConverterFactory(GsonConverterFactory.create(gson)).
If you still want to receive an string just use .addConverterFactory(ScalarsConverterFactory.create()) as explained here.
I'm trying to write an interceptor that compresses a request body using Gzip.
My server does not support compressed requests, so I'll be using an application/octet-stream instead of Content-Type: gzip and compress the request body manually, it will be decompressed manually at backend.
public class GzipRequestInterceptor implements Interceptor {
final String CONTENT_TYPE = "application/octet-stream";
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (originalRequest.body() == null || CONTENT_TYPE.equals(originalRequest.header("Content-Type"))) {
return chain.proceed(originalRequest);
}
Request compressedRequest = originalRequest.newBuilder()
.header("Content-Type", CONTENT_TYPE)
.method(originalRequest.method(), gzip(originalRequest.body()))
.build();
return chain.proceed(compressedRequest);
}
private RequestBody gzip(final RequestBody body) throws IOException {
final Buffer inputBuffer = new Buffer();
body.writeTo(inputBuffer);
final Buffer outputBuffer = new Buffer();
GZIPOutputStream gos = new GZIPOutputStream(outputBuffer.outputStream());
gos.write(inputBuffer.readByteArray());
inputBuffer.close();
gos.close();
return new RequestBody() {
#Override
public MediaType contentType() {
return body.contentType();
}
#Override
public long contentLength() {
return outputBuffer.size();
}
#Override
public void writeTo(BufferedSink sink) throws IOException {
ByteString snapshot = outputBuffer.snapshot();
sink.write(snapshot);
}
};
}
}
It doesn't work - 30 seconds after request is fired, a 500 Server Error is received. On the server there's a timeout exception.
My guess is that I've done wrong with input/output on the gzip method... any ideas?
Update if I stop the app, the request goes through successfully, does this indicate that the app is still waiting for data from outputBuffer?
Is this what you are looking for Interceptors, check the Rewriting Requests -chapter.
I'm in the midst of testing my application which is using an HTTP-server. Instead of mocking I decided to go with a HTTP server fixture. Meaning that I do not have to mock any productional code. To accomplish this goal I currently chose for a free to use 3rd party library fixd.
I was able to successfully create several unit tests - which are working by means of a GET request. Most are quite simple, i.e.:
#Test
public void verifyConnectionTest()
{
try
{
final String body = FileUtils.readFileToString(RESOURCE);
final String path = "/";
this.server.handle(Method.GET, path).with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response)
{
response.setStatusCode(200);
response.setContentType("text/xml");
response.setBody(body);
}
});
// Setting up my HTTP client
// Execute some tasks
// asserting of everything was valid
}
catch (final IOException e)
{
fail(e.getMessage());
}
}
But I now have to send a POST request with multipart/form-data. Which does not make much of a difference other than changing the method and content-type:
#Test
public void executeStepTest()
{
try
{
final String body = FileUtils.readFileToString(SERVICE_RESPONSE);
final String path = "/";
this.server.handle(Method.POST, path, "multipart/form-data").with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response)
{
response.setStatusCode(200);
response.setContentType("text/xml");
response.setBody(body);
}
});
// Setting up my HTTP client
// Execute some tasks
// asserting of everything was valid
}
catch (final IOException e)
{
fail(e.getMessage());
}
}
However I get the following error: [ERROR] could not find a handler for POST - / - multipart/form-data; boundary=bqCBI7t-VW1xaJW7BADmTiGMg9w_YM2sHH8ukJYx and my guess is that fixd doesn't recognize the boundary-party. Since the documentation does not show an example I'm quite stuck on this part.
I tried using some wildcards such as '*', no succes. Thus; I need a way to either tell fixd to accept that boundary or use some wildcards I didn't yet discover. Any help would be great, thanks!
I've been making some debug and it seems to be that the problem is in the fixd core.
Basically, fixd indexes every RequestHandlerImpl by a HandlerKey (which includes ContentType as part of the key) in the map handlerMap. See method org.bigtesting.fixd.core.FixtureContainer#resolve.
...
HandlerKey key = new HandlerKey(method, route, contentType);
RequestHandlerImpl handler = handlerMap.get(key);
if (handler == null) {
// Error
}
...
Problem: When the request is multipart/form-data, boundary data (which it's generated dinamically every request) is part of the content type. So, any handler is found in handlerMap because the key changes with every running.
I've made a little test only to check that this is the cause of the problem, passing the contentType to fixd server.handle after the creation of the multipart request, and it works fine.
See the test below:
#Test
public void verifyConnectionTest_multipart() {
try {
// 1. Create multipart request (example with http-commons 3.1)
PostMethod filePost = new PostMethod(url);
Part[] parts = { new StringPart("param", "value") };
MultipartRequestEntity request = new MultipartRequestEntity(parts, filePost.getParams());
filePost.setRequestEntity(request);
// 2. fixd server handle (passing the request content type)
this.server.handle(Method.POST, "/", request.getContentType()).with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response) {
response.setStatusCode(200);
response.setContentType("text/xml");
}
});
// 3. Execute multipart request
HttpClient client = new HttpClient();
int status = client.executeMethod(filePost);
// 4. Assertions
Assert.assertEquals(200, status);
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
Hope it helps you to clarify the problem. Cheers
This was a bug in fixd, and has been fixed in version 1.0.3. Your original code should work using this new version of fixd.
I am trying to use RequestBuilder in GWT to see if Accept-Ranges is supported.
Following is my client code:
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,pathToServlet);
builder.setHeader("Range","bytes=0-10");
RequestCallback callback = new RequestCallback() {
#Override
public void onError(Request arg0, Throwable arg1) {
}
#Override
public void onResponseReceived(Request req, Response res) {
log.info("Text:"+res.getText());
log.info("Code:"+res.getStatusCode());
}
};
try {
builder.sendRequest(null, callback);
} catch (RequestException e) {}
And my servlet code is just a simple test code:
public class RangeTest extends HttpServlet{
static Logger log = Logger.getLogger(RangeTest.class);
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String output = new String("This is a test string to be sent to the client");
response.setContentType("text/xml");
PrintWriter out = response.getWriter();
out.println(output);
}
}
In the output I get on the client, following is printed:
Text:This is a test string to be sent to the client
Code:200
What I expected was, since I gave the range header as 0-10 in the request, only the first 10 bytes will be sent to the client. But here the entire string is getting sent. What am I doing wrong here? Is there anything I have missed?
I feel my comment is more readable for other as answer (and effectively it is one):
You are not evaluating the range-header in your servlet-method. And the super class HttpServlet does not evaluate it either (but DefaultServlet from Tomcat).
The servlet specification has left most of the implementation work to providers like Apache. This explains why API classes like HttpServlet does not do the work of interpreting special http headers, but provider classes like the mentioned Tomcat-DefaultServlet. The main purpose of a specification is mainly to enable different implementations not to force people to only one.