rest icon indicating copy to clipboard operation
rest copied to clipboard

Response.getLength() limited to int max value (~2GB)

Open maximebochon opened this issue 3 years ago • 5 comments

The "Content-Length" HTTP header provides the length of the body expressed in bytes. There seems to be no limitation in the HTTP specifications about that value.

In the JAX-RS API, this value is available through the getter getLength() of the Response class, and the returned value is an int (32-bits signed integer) which support a maximum positive value of 2 147 483 647. It's around 2 GB, so it can easily be exceeded nowadays.

An improvement would be that the getLength() method return a long which would push the limit to 9 EB (1018 bytes).

This is what is done by the Apache HttpClient implementation (see org.apache.http.HttpEntity.getContentLength() here).

maximebochon avatar Apr 14 '21 17:04 maximebochon

Changing the signature of int getLength() to long getLength() would require all classes extending Response to be changed. On example is InboundJaxrsResponse in Jersey.

I am not sure if this is a good idea.

ghost avatar Apr 14 '21 18:04 ghost

Of course changing the API does have impacts. Why would it not be a good idea though?

Am I in the right place to ask for a correction of small flaw in the JAX-RS API?

Also there may have been real motivations for int instead of long, that I'm not aware of, when the API was designed, and I'm quite inclined to hear them.

Another approach would be be to add a long getLengthLong() in the way it is done here for Servlets.

maximebochon avatar Apr 15 '21 07:04 maximebochon

Am I in the right place to ask for a correction of small flaw in the JAX-RS API?

Yep, you're in the right place. :)

Of course changing the API does have impacts. Why would it not be a good idea though?

If we were designing from scratch, I would agree that long is preferable to int, but making that change now would break backwards compatibility.

Another approach would be be to add a long getLengthLong() in the way it is done here for Servlets.

I think this is reasonable. It resolves the backwards compatibility issue and still provides the convenience of returning large content length values.

In the meantime, if you need a workaround on the existing APIs, something like this might help you:

public static long getLengthLong(Response r) {
    String contentLength = r.getHeaderString(HttpHeaders.CONTENT_LENGTH);
    if (contentLength != null) try {
        return Long.parseLong(contentLength);
    } catch (NumberFormatException ignore) {}
    return -1;
}

andymc12 avatar Apr 15 '21 15:04 andymc12

Of course changing the API does have impacts. Why would it not be a good idea though?

As JAX-RS is not just some library you can simply switch in your app, but actually is contract between millions of existing applications and the existing application servers, non-backwards-compatible changes are simply a no-go for us (unless we defer your request to a major version like 4.x, which is far, far away). But we can add new methods, certainly. :-)

Having said that I am +-0 for your proposal of adding getLengthLong for the time being, i. e. it would be OK for me but I also can wait for 4.0.

mkarg avatar Apr 15 '21 17:04 mkarg

Thank you @andymc12 for the workaround snippet.

Actually, I had already done something similar, but using much 3rd-party syntactic sugar:

import com.google.common.primitives.Longs;

import static com.google.common.base.Strings.nullToEmpty;
import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
import static org.apache.commons.lang3.math.NumberUtils.INTEGER_ZERO;

...

final Long httpContentlength = Longs.tryParse(nullToEmpty(httpResponse.getHeaderString(CONTENT_LENGTH)));

if (httpContentlength == null || httpContentlength < INTEGER_ZERO)
{
   throw new DepotFichierException("Abandon de la recopie du fichier. Sa taille n'est pas fournie par l'entête HTTP.");
}

maximebochon avatar Apr 16 '21 15:04 maximebochon