Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Test gzip support in RESTEasy Reactive #16924

Closed
wants to merge 9 commits into from
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package io.quarkus.resteasy.reactive;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.function.Supplier;

import javax.imageio.ImageIO;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.hamcrest.Matchers;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

public class GZipTest {

private static final String APP_PROPS = "" +
"quarkus.http.enable-compression=true\n";
Copy link
Contributor

@netodevel netodevel Apr 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think of a test with the other way?

quarkus.http.enable-compression=false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is disabled, should we really perform any sort of test? I don't think that's needed when is not even enabled :)

Copy link
Contributor

@netodevel netodevel May 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but how to ensure that the property is working correctly?
and how do you make sure that no future code from other developers doesn't affect this and go undetected because not have any tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FroMage WDYT

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, we should test the default behaviour, that's true, but because we know what the default is ATM, we want to test the default behaviour without any configuration, to make sure it won't be compressed by default.


static String longString;
static {

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; ++i) {
sb.append("Hello RESTEasy Reactive;");
}
longString = sb.toString();
}

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest()
.setArchiveProducer(new Supplier<JavaArchive>() {
@Override
public JavaArchive get() {
return ShrinkWrap.create(JavaArchive.class)
.addAsResource(new StringAsset(APP_PROPS), "application.properties")
.addClasses(TestCompression.class);
}
});

@Test
public void testServerCompression() throws Exception {

RestAssured.given().get("/test/compression").then().statusCode(200)
.header("content-encoding", "gzip")
.header("content-length", Matchers.not(Matchers.equalTo(Integer.toString(longString.length()))))
.body(Matchers.equalTo(longString));

RestAssured.given().get("/test/nocompression").then().statusCode(200)
.header("content-encoding", "identity")
.header("content-length", Matchers.equalTo((long) (createImage().getData().getDataBuffer().getSize() * 4L)))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(long) (createImage().getData().getDataBuffer().getSize() * 4L)

Finding the content-length for BufferedImage is something I am not much sure about.
I took reference from here -
https://stackoverflow.com/questions/632229/how-to-calculate-java-bufferedimage-filesize?noredirect=1&lq=1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The size of this png image(original) is 2.91kb OR 2910 bytes

Using the above technique, the size is 360000 bytes.

On calculating content-length using ByteArrayOutputStream I am getting 2914 bytes. ✔️
So perhaps this is the correct techniques to get the byte size of BufferedImage :

            BufferedImage image = createImage();
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ImageIO.write(image, "png", outputStream);
            outputStream.close();
            long contentLength = outputStream.size();
            System.out.println(" content-length using ByteArrayOutputStream => " + contentLength );


image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually you can just use a static test image as a resource :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Humm I thought it's better to generate an image only when this test is run.

You are right, it will make code simpler. Instead of creating an image through code it is better to use a static image 💯

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Original size - 2914 bytes
Compressed size - 2741 bytes
image

.body(Matchers.equalTo(createImage()));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So does this test pass? I don't see anything in the endpoints that would justify a different behaviour.
Could you test with returning a binary reponse, such as an image? Just to see what vert.x does in this case, if it stll tries to compress it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
It is failing with this info.
For endpoint /test/nocompression

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created an image and returned to the endpoint. This time also, content-encoding is gzip.
image

Copy link
Contributor Author

@saumya1singh saumya1singh May 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in the beginning I am initializing APP_PROPS as true

private static final String APP_PROPS = "" +
            "quarkus.http.enable-compression=true\n";

perhaps that's why content encoding is always gzip even for Binary Data i.e Images

That means vert.x is trying to compress the binary data as well, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I set quarkus.http.enable-compression to false , the content-length=24000 bytes which is far more than 130 bytes (for text) or 165 bytes(for image)

private static final String APP_PROPS = "" +
            "quarkus.http.enable-compression=false\n";

image

Copy link
Contributor Author

@saumya1singh saumya1singh May 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So perhaps the conclusion is - It is trying to compress images as well.

Am I right @FroMage?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, probably, could you add that test to your PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sure, I missed this comment.


@Path("/test")
public static class TestCompression {

@Path("/compression")
@GET
public String registerCompression() {
return longString;
}

@Path("/nocompression")
@GET
public BufferedImage registerNoCompression() throws IOException {
BufferedImage image = createImage();
return image;
}
}

public static BufferedImage createImage() throws IOException {
int width = 300;
int height = 300;

BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Create a graphics which can be used to draw into the buffered image
Graphics2D graphic = bufferedImage.createGraphics();
// fill all the image with white
graphic.setColor(Color.white);
graphic.fillRect(0, 0, width, height);

// create a circle with black
graphic.setColor(Color.black);
graphic.fillOval(0, 0, width, height);

// create a string with yellow
graphic.setColor(Color.yellow);
graphic.drawString("Quarkus RESTEasy Reactive", 50, 120);
graphic.dispose();

// Save as PNG
File file = new File("src/test/resources/imageForTest.png");
ImageIO.write(bufferedImage, "png", file);
return bufferedImage;
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The image I am creating in createImage() looks like this -

image

Perhaps "saving the image" is optional.