Arsen Zahray
Arsen Zahray

Reputation: 25287

How do I read HttpEntity without consuming it?

I've got org.apache.http.HttpResponse object, which I'm using at different places in the code. One of those places is for logging.

The problem is that when I run following log code:

HttpEntity entity = response.getEntity();
try {
    String content = Base64.encodeToString(
            EntityUtils.toByteArray(entity), Base64.DEFAULT);
    sb.append(content + "\r\n");
} catch (Exception e) {
    sb.append("\r\n\r\n====EXCEPTION=====\r\n" + e.toString()
            + "\r\n");
}

and than I try to read entry content in the actual processing code, that causes the code to throw following exception:

java.lang.IllegalStateException: Content has been consumed

My question is: how do I read the entity without consuming it in the log code?

UPDATE here's the full code of the function I use to transform httpresponse to string:

static String toString(org.apache.http.HttpResponse response) {
    try {
        if (response == null) {
            return "null";
        }

        StringBuilder sb = new StringBuilder();
        sb.append("==============BEGIN HttpResponse================\r\n");
        StatusLine sl = response.getStatusLine();
        if (sl == null) {
            sb.append("status line is null\r\n");
        } else {
            sb.append(String.format("%s %s\r\n", sl.getStatusCode(),
                    sl.getReasonPhrase()));
        }

        for (Header h : response.getAllHeaders()) {
            if (h == null) {
                sb.append("header is null\r\n");
                continue;
            }
            sb.append(String.format("%s: %s\r\n", h.getName(), h.getValue()));
        }

        sb.append("\r\r\r\n");

        HttpEntity entity = response.getEntity();
        if (entity == null) {
            sb.append("content is null");
        } else {
            try {
                String content = Base64.encodeToString(
                        EntityUtils.toByteArray(entity), Base64.DEFAULT);

                sb.append(content + "\r\n");
            } catch (Exception e) {
                sb.append("\r\n\r\n====EXCEPTION=====\r\n" + e.toString()
                        + "\r\n");
            }
        }

        sb.append("\r\n==============END HttpResponse================\r\n");

        return sb.toString();
    } catch (Exception e) {
        return e.toString();
    }
}

Upvotes: 0

Views: 3708

Answers (2)

Ed Loktseu
Ed Loktseu

Reputation: 31

It might give you some performance issues, but will work: Example of my HttpClient with logging.

private CloseableHttpResponse invoke(HttpRequestBase http) {
        try {
            CloseableHttpResponse response = client.execute(http);
            if (http instanceof HttpPost) {
                InputStream inputStream = ((HttpPost) http).getEntity().getContent();
                String body = IOUtils.toString(inputStream, Charset.defaultCharset());

                HttpEntity respBody = response.getEntity();
                String responseBody = StreamUtils.copyToString(respBody.getContent(), Charset.defaultCharset());
                response.setEntity(new StringEntity(responseBody));
                LOG.info(String.format("Sending request: [%s] %s => [%s] \nPayload:\n%s \nResponse:\n%s", http.getMethod(), http.getURI(), response.getStatusLine(), body, responseBody));
            } else {
                LOG.info(String.format("Sending request: [%s] %s => [%s]", http.getMethod(), http.getURI(), response.getStatusLine()));
            }
            return response;
        } catch (IOException e) {
            throw new RuntimeException("HTTP request failed: " + http.toString(), e);
        }
    }

Main idea is following:
1. make http call
2. copy to string your response body:

 HttpEntity respBody = response.getEntity();  
 String responseBody = StreamUtils.copyToString(respBody.getContent(), Charset.defaultCharset());
  1. log it
  2. set new response entity like response.setEntity(new StringEntity(responseBody));

This example work good for small test framework, not sure it's good code for production application

Upvotes: 1

Arsen Zahray
Arsen Zahray

Reputation: 25287

Ok. So what I ended up doing is implementing my own HttpEntity class, and than just using response.setEntity(...) to replace the previous entity. That class stores the result as binary array and returns it as many times as necessary.

Upvotes: 1

Related Questions