Skip to content

Commit

Permalink
fixup! feat: set Content-Type header to application/gzip|zstd when th…
Browse files Browse the repository at this point in the history
…e compression property in the request was set #665
  • Loading branch information
fengelniederhammer committed Feb 29, 2024
1 parent ca324fa commit 5b6ffdc
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,19 @@ fun ZstdOutputStream.commitUnderlyingResponseToPreventContentLengthFromBeingSet(

@Component
@RequestScope
class RequestCompression(var compression: Compression? = null)
class RequestCompression(var compressionSource: CompressionSource = CompressionSource.None)

sealed interface CompressionSource {
data class RequestProperty(override var compression: Compression) : CompressionSource

data class AcceptEncodingHeader(override var compression: Compression) : CompressionSource

data object None : CompressionSource {
override val compression = null
}

val compression: Compression?
}

@Component
@Order(COMPRESSION_FILTER_ORDER)
Expand Down Expand Up @@ -156,7 +168,7 @@ class CompressionFilter(val objectMapper: ObjectMapper, val requestCompression:
if (compressionPropertyInRequest != null) {
log.info { "Compressing using $compressionPropertyInRequest from request property" }

requestCompression.compression = compressionPropertyInRequest
requestCompression.compressionSource = CompressionSource.RequestProperty(compressionPropertyInRequest)
return CompressingResponse(
response,
compressionPropertyInRequest,
Expand All @@ -168,9 +180,11 @@ class CompressionFilter(val objectMapper: ObjectMapper, val requestCompression:

log.info { "Compressing using $compression from $ACCEPT_ENCODING header" }

response.setHeader(CONTENT_ENCODING, compression.value)
requestCompression.compression = compression
requestCompression.compressionSource = CompressionSource.AcceptEncodingHeader(compression)
return CompressingResponse(response, compression, contentType = null)
.apply {
setHeader(CONTENT_ENCODING, compression.value)
}
}
}

Expand Down Expand Up @@ -239,7 +253,7 @@ class CompressionAwareMappingJackson2HttpMessageConverter(
private val requestCompression: RequestCompression,
) : MappingJackson2HttpMessageConverter(objectMapper) {
override fun canWrite(mediaType: MediaType?): Boolean {
if (requestCompression.compression?.contentType?.isCompatibleWith(mediaType) == true) {
if (requestCompression.compressionSource.compression?.contentType?.isCompatibleWith(mediaType) == true) {
return true
}

Expand All @@ -251,9 +265,9 @@ class CompressionAwareMappingJackson2HttpMessageConverter(
value: Any,
contentType: MediaType?,
) {
val compression = requestCompression.compression
if (compression != null && contentType != compression.contentType) {
headers.set(CONTENT_ENCODING, compression.value)
val compressionSource = requestCompression.compressionSource
if (compressionSource is CompressionSource.RequestProperty && compressionSource.compression.contentType != contentType) {
headers.set(CONTENT_ENCODING, compressionSource.compression.value)
}

super.addDefaultHeaders(headers, value, contentType)
Expand All @@ -269,7 +283,7 @@ class StringHttpMessageConverterWithUnknownContentLengthInCaseOfCompression(
str: String,
contentType: MediaType?,
): Long? {
return when (requestCompression.compression) {
return when (requestCompression.compressionSource.compression) {
null -> super.getContentLength(str, contentType)
else -> null
}
Expand Down
44 changes: 38 additions & 6 deletions siloLapisTests/test/common.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from 'chai';
import { basePath, expectIsZstdEncoded } from './common';
import { basePath, expectIsGzipEncoded, expectIsZstdEncoded } from './common';

const routes = [
{ pathSegment: '/aggregated', servesFasta: false, expectedDownloadFilename: 'aggregated.json' },
Expand Down Expand Up @@ -45,12 +45,12 @@ describe('All endpoints', () => {
for (const route of routes) {
const url = `${basePath}/sample${route.pathSegment}`;

function get(params?: URLSearchParams) {
function get(params?: URLSearchParams, requestInit?: RequestInit) {
if (params === undefined) {
return fetch(url);
return fetch(url, requestInit);
}

return fetch(url + '?' + params.toString());
return fetch(url + '?' + params.toString(), requestInit);
}

describe(`(${route.pathSegment})`, () => {
Expand All @@ -72,22 +72,54 @@ describe('All endpoints', () => {
expect(response.headers.get('lapis-data-version')).to.match(/\d{10}/);
});

it('should return zstd compressed data', async () => {
it('should return zstd compressed data when asking for compression', async () => {
const urlParams = new URLSearchParams({ compression: 'zstd' });

const response = await get(urlParams);

expect(response.status).equals(200);
expect(response.headers.get('content-type')).equals('application/zstd');
expect(response.headers.get('content-encoding')).does.not.exist;
expectIsZstdEncoded(await response.arrayBuffer());
});

it('should return zstd compressed data when accepting compression in header', async () => {
const urlParams = new URLSearchParams();

const response = await get(urlParams, { headers: { 'Accept-Encoding': 'zstd' } });

expect(response.status).equals(200);
if (route.servesFasta) {
expect(response.headers.get('content-type')).equals('text/x-fasta;charset=UTF-8');
} else {
expect(response.headers.get('content-type')).equals('application/json');
}
expect(response.headers.get('content-encoding')).equals('zstd');
expectIsZstdEncoded(await response.arrayBuffer());
});

it('should return gzip compressed data', async () => {
it('should return gzip compressed data when asking for compression', async () => {
const urlParams = new URLSearchParams({ compression: 'gzip' });

const response = await get(urlParams);

expect(response.status).equals(200);
expect(response.headers.get('content-type')).equals('application/gzip');
expect(response.headers.get('content-encoding')).does.not.exist;
expectIsGzipEncoded(await response.arrayBuffer());
});

it('should return gzip compressed data when accepting compression in header', async () => {
const urlParams = new URLSearchParams();

const response = await get(urlParams, { headers: { 'Accept-Encoding': 'gzip' } });

expect(response.status).equals(200);
if (route.servesFasta) {
expect(response.headers.get('content-type')).equals('text/x-fasta;charset=UTF-8');
} else {
expect(response.headers.get('content-type')).equals('application/json');
}
expect(response.headers.get('content-encoding')).equals('gzip');

if (route.servesFasta) {
Expand Down
6 changes: 6 additions & 0 deletions siloLapisTests/test/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,9 @@ export function expectIsZstdEncoded(arrayBuffer: ArrayBuffer) {

expect([...first4Bytes]).deep.equals([Number('0x28'), Number('0xb5'), Number('0x2f'), Number('0xfd')]);
}

export function expectIsGzipEncoded(arrayBuffer: ArrayBuffer) {
const first2Bytes = new Uint8Array(arrayBuffer).slice(0, 2);

expect([...first2Bytes]).deep.equals([Number('0x1f'), Number('0x8b')]);
}

0 comments on commit 5b6ffdc

Please sign in to comment.