0

I am trying to get HTTP response with the help of apache httpclient. I get headers successfully but it throws exception when I try to get contents. Exception is:

Caused by: org.apache.http.ConnectionClosedException: Premature end of Content-Length delimited message body (expected: 42 320; received: 7 787)
[INFO] [talledLocalContainer]   at org.apache.http.impl.io.ContentLengthInputStream.read(ContentLengthInputStream.java:178) ~[httpcore-4.4.15.jar:4.4.15]
[INFO] [talledLocalContainer]   at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:135) ~[httpclient-4.5.13.jar:4.5.13]
[INFO] [talledLocalContainer]   at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:148) ~[httpclient-4.5.13.jar:4.5.13]
[INFO] [talledLocalContainer]   at com.blueway.common.utils.BWFileUtils.writeToFile(BWFileUtils.java:207) ~[engine-module-common-0.0.1-SNAPSHOT.jar:?]
[INFO] [talledLocalContainer]   at com.blueway.platform.app.engine.technicalconnector.impl.service.azureblobstorage.AzureBlobStorageService.getBlob(AzureBlobStorageService.java:49) ~[web-0.0.1-SNAPSHOT.jar:?]
[INFO] [talledLocalContainer]   at com.blueway.platform.app.engine.technicalconnector.impl.service.azureblobstorage.invoker.AzureBlobStorageGetBlobInvoker.invoke(AzureBlobStorageGetBlobInvoker.java:22) ~[web-0.0.1-SNAPSHOT.jar:?]
[INFO] [talledLocalContainer]   at com.blueway.platform.app.engine.technicalconnector.impl.service.azureblobstorage.AzureBlobStorageInvokeWrapper.doInvoke(AzureBlobStorageInvokeWrapper.java:34) ~[web-0.0.1-SNAPSHOT.jar:?]
[INFO] [talledLocalContainer]   at com.blueway.engine52.support.ConnectorAzureBlobStorage.invoke(ConnectorAzureBlobStorage.java:46) ~[web-0.0.1-SNAPSHOT.jar:?]
[INFO] [talledLocalContainer]   at com.blueway.engine52.service.instruction.ConnectorHandlingInstruction.invokeConnector(ConnectorHandlingInstruction.java:68) ~[web-0.0.1-SNAPSHOT.jar:?]
[INFO] [talledLocalContainer]   at com.blueway.engine52.service.instruction.ConnectorHandlingInstruction.executeInstructionWithSupport(ConnectorHandlingInstruction.java:50) ~[web-0.0.1-SNAPSHOT.jar:?]
[INFO] [talledLocalContainer]   at com.blueway.engine52.service.instruction.ConnectorHandlingInstruction.executeInstructionWithSupport(ConnectorHandlingInstruction.java:19) ~[web-0.0.1-SNAPSHOT.jar:?]
[INFO] [talledLocalContainer]   at com.blueway.engine52.service.instruction.SupportHandlingInstruction.executeInstruction(SupportHandlingInstruction.java:99) ~[web-0.0.1-SNAPSHOT.jar:?]
[INFO] [talledLocalContainer]   at com.blueway.engine52.service.instruction.Instruction.tryExecuteInstruction(Instruction.java:363) ~[web-0.0.1-SNAPSHOT.jar:?]

and the code is

public CloseableHttpResponse get(final String containerName, final String blobName) throws AzBException{
    final Logger logger = LoggerFactory.getLogger(getClass());

    final String url = HTTPS + accountName + END_POINT + containerName + "/" + blobName;
    try (final CloseableHttpClient httpClient = HttpClients.createDefault()) {
        final HttpGet httpGet = new HttpGet(url);
        logger.debug("Sending HTTP GET request to URL: {}", url);
        final CloseableHttpResponse  response = httpClient.execute(httpGet);
        logger.debug("Received HTTP response with status code: {}", response.getStatusLine().getStatusCode());
        return response;
    } catch (IOException e) {
        throw new AzBException("An.get HttpGet error: " + e.getCause().getMessage(), e);
    }
}

any help will be appreciated. thanks

Arun Sudhakaran
  • 2,167
  • 4
  • 27
  • 52
IphoneVore
  • 7
  • 1
  • 6

2 Answers2

0

Do you have success with implementing this requirement using the Http classes packaged with Java? Sorry, it's just... I'm not personally a fan of Apache HttpClient. The last time I used it it was extremely unstable, bugs across different versions, and I just ended up abandoning it and implementing something similar myself.

You can easily do a Http Get with Java as follows:

package util;

import org.junit.jupiter.api.Test;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.time.temporal.ChronoUnit;

public class QuickTest {

    @Test
    public void test() throws Exception {
        String url = "https://www.google.com";

        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI(url))
                .version(HttpClient.Version.HTTP_2)
                .timeout(Duration.of(30, ChronoUnit.SECONDS))
                .GET()
                .build();

        HttpResponse<String> response = HttpClient.newBuilder()
                .build()
                .send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println("Got body: " + response.body());
    }
}

Check out [this article] for more details. In older code that implements something similar you might see the use of the HttpURLConnection class. The above code snippet uses the Java 11 classes that support HTTP/2 and Web Socket.

Sorry for not answering your question, I'm just not a fan of Apache HttpClient. And hopefully you can agree that the implementation given above is already quite succinct and easy to understand so one doesn't really need an external library.

The creation of Apache HttpClient precedes the release of the Java 11 classes used above and were probably implemented to abstract developers away from the complexity of the now deprecated HttpURLConnection.

Do to the problems I've encountered working with these things I have also abstracted the implementation into a wrapper. So my client code (business logic code) uses the wrapper and doesn't get directly linked to the classes of the implementation I'm actually using, whether that be Apache HttpClient or Java.

So in my code you might see:

HttpResponse httpResponse = HttpGet.withEndpointUrl(url).execute();
System.out.println("Got body: " + httpResponse.getBody());

Where HttpResponse and HttpGet is my own classes. So the client code doesn't know what implementation is being used and it doesn't link the implementation to the classes of that implementation. So if I ever need to refactor the implementation, then I can just refactor the implementation, and not need to refactor all my client code as well.

dutoitns
  • 1,949
  • 1
  • 26
  • 32
  • And with bugs, I mean...runtime bugs. Everything would compile, a lot of requests would succeed. But then occasionally certain requests would just bomb out. Rolled my own implementation (back then using [HttpURLConnection](https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html)) and all the problems got resolved. – dutoitns Jul 04 '23 at 12:12
0

Your code:

logger.debug("Received HTTP response with status code: {}", response.getStatusLine().getStatusCode());

Does it print the status code for you at least?

If you try to print the body at the same location - does that work?

Maybe it only fails subsequently outside of the method? The CloseableHttpClient class is probably representative of the connection with the endpoint once you execute the request. I see it implements the AutoClosable interface. So CloseableHttpClient, representing the connection, will be closed when you exit your try section.

From the [AutoClosable] Javadoc:

An object that may hold resources (such as file or socket handles) until it is closed. The close() method of an AutoCloseable object is called automatically when exiting a try-with-resources block for which the object has been declared in the resource specification header. This construction ensures prompt release, avoiding resource exhaustion exceptions and errors that may otherwise occur.

If this is the issue, maybe you can create a Messenger object (just an object encapsulating data so that it can be passed around) and populate it with the contents of the CloseableHttpResponse immediately, and return the Messenger object from the method. Remember to close the CloseableHttpResponse.

An example Messenger:

public class HttpResponse {
    
    private final static Logger logger = LoggerFactory.getLogger(HttpResponse.class);

    private final int statusCode;
    private final String responseBody;
    private final Map<String, String> headerList;
    
    public HttpResponse(int statusCode) {
        this(statusCode, null);
    }
    public HttpResponse(int statusCode, CharSequence responseBody) {
        this.statusCode = statusCode;
        this.responseBody = responseBody != null ? responseBody.toString() : null;
        this.headerList = null;
    }
    
    /** Implementation assumes there is only one header with the specified key **/
    public String getHeaderNullIfNotValid(String headerKey) {
        Assert.isNotNull(headerKey);
        for (Map.Entry<String, String> currHeader : headerList.entrySet()) {
            if (headerKey.equals(currHeader.getKey()))
                return currHeader.getValue();
        }
        return null;
    }

    public boolean isOk() {
        return (statusCode == HttpStatusCode.OK.getCode());
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Debugging Http response: ");
        sb.append("\n\t Status code : " + this.statusCode + " ");
        sb.append("\n\t Response body : " + this.responseBody + " ");
        if (headerList == null || headerList.size() == 0)
            sb.append("\n\t No headers.");
        else {
            sb.append("\n\t Headers:");
            for (Map.Entry<String, String> currHeader : headerList.entrySet()) {
                sb.append("\n\t\t " + currHeader.getKey() + " => " + currHeader.getValue());
            }
        }
        return sb.toString();
    }
    
    // --- Attributes -------------------------------------------------------------------------------------------------
    
    public int getStatusCode() {
        return statusCode;
    }
    public String getBody() {
        return responseBody;
    }
    public Map<String, String> getHeaderList() {
        return headerList;
    }
}
dutoitns
  • 1,949
  • 1
  • 26
  • 32