Dropwizard decompress request filter

Ram Kumar :

I have a dropwizard application where the client request body content is gzipped content. I need to decompress the content in dropwizard app. I have the following code, but I am getting an exception java.io.EOFException at the line GZIPInputStream is = new GZIPInputStream(new ByteArrayInputStream(gzipBody))

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.util.zip.GZIPInputStream;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;

@Path("/")
public class UserEventResource {
    @POST
    @Path("/save")
    @Produces("application/json;charset=utf-8")
    public Response save(byte[] gzipBody) {
        try {
            try (GZIPInputStream is = new GZIPInputStream(new ByteArrayInputStream(gzipBody))) {
                try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
                    byte[] buffer = new byte[4096];
                    int length;
                    while ((length = is.read(buffer)) > 0) {
                        os.write(buffer, 0, length);
                    }
                    String body = new String(os.toByteArray(), Charset.forName("UTF-8"));
                }
            }
            return Response.status(OK).build();
        } catch (Exception exception) {
            return Response.status(INTERNAL_SERVER_ERROR).build();
        }
    }
}

Client is sending the following request,

curl -XPOST -d @test.gz http://localhost:8080/save

test.gz is created by following step,

echo "hello world" > test
gzip test
Paul Samsotha :

The code itself works. The problem in this question is the cURL request. If you add the -v (verbose) flag, you'll see the problem.

$ curl -XPOST -v -d @test.gz http://localhost:8080/api/gzip/save
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> POST /api/gzip/save HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Length: 8
> Content-Type: application/x-www-form-urlencoded

The problem is in the last line: the Content-Type is application/x-www-form-urlencoded. Not just this though, but the data in the file isn't sent either. I don't know the specifics, but it has something to do with the -d flag. The default to cURL is to send application/x-www-form-urlencoded data when using the -d flag.

What we should be doing is using the --data-binary option instead of -d and also set the Content-Type to application/octet-stream. This will also cause the correct provider to be called on the server side.

curl -XPOST -v \
     -H 'Content-Type:application/octet-stream' \
     --data-binary @test.gz \
     http://localhost:8080/api/gzip/save

And just to be sure that our endpoint only accepts application/octet-stream, we should add the @Consumes annotation. This is important, as we don't want random providers to be called, which can lead to weird error messages.

@POST
@Path("/save")
@Produces("application/json;charset=utf-8")
@Consumes("application/octet-stream")
public Response save(byte[] gzipBody) {

}

Asides

  • I wouldn't use a byte[] parameter for the method. You don't really want the entire file to be read into memory. Sure the example already reads it to get the String. But most likely in a real application, you are going to saving the file somewhere. So instead of a byte[] parameter, just use an InputStream. You can pass that InputStream to the GZIPInputStream constructor.

  • For uploading files, consider using multipart instead. With multipart you can not just send more than one file at a time, but you can also add meta data to the files. See Jersey support and Dropwizard support (which isn't much just a bundle wrapper around the Jersey feature.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=97133&siteId=1