Skip to content

Instantly share code, notes, and snippets.

@StFS
Created January 15, 2021 17:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save StFS/90d090cb811cb25d2d9fde35e838704c to your computer and use it in GitHub Desktop.
Save StFS/90d090cb811cb25d2d9fde35e838704c to your computer and use it in GitHub Desktop.
package com.genuitysci.fileservice.interceptor
import io.quarkus.runtime.configuration.MemorySize
import org.eclipse.microprofile.config.ConfigProvider
import org.jboss.resteasy.resteasy_jaxrs.i18n.Messages
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.math.BigInteger
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import javax.ws.rs.WebApplicationException
import javax.ws.rs.core.HttpHeaders
import javax.ws.rs.core.Response
import javax.ws.rs.core.Response.Status.*
import javax.ws.rs.ext.*
/**
* Custom gzip support, workaround for https://github.com/quarkusio/quarkus/issues/12647
* - even though the issue was closed, it's not fixed!
*
* NB: should be deleted when quarkus will have proper built-in gzip support.
*/
@Provider
class GZIPInterceptor : ReaderInterceptor, WriterInterceptor {
// The below properties are lazy-loaded in this weird way to support native image building.
// The native build initializes all classes and if these properties were initialized in a
// constructor the would be initialized at build time and not runtime.
private var _isEnabled: Boolean? = null
private val isEnabled: Boolean
get() {
if (_isEnabled == null) {
val quarkusResteasyGzipEnabled =
ConfigProvider.getConfig().getOptionalValue("quarkus.resteasy.gzip.enabled", Boolean::class.java)
val ftsResteasyGzipEnabled = ConfigProvider.getConfig()
.getOptionalValue("nextcode.fts.resteasy.gzip.enabled", Boolean::class.java)
ftsResteasyGzipEnabled.ifPresent {
quarkusResteasyGzipEnabled.ifPresent {
if (quarkusResteasyGzipEnabled.get()) {
throw IOException("Can't have both Quarkus and FTS gzip interceptors enabled")
}
}
_isEnabled = ftsResteasyGzipEnabled.get()
}
}
return _isEnabled as Boolean
}
private var _maxInput: MemorySize? = null
private val maxInputBytes: Long
get() {
if (_maxInput == null) {
val ftsResteasyGzipMaxInput = ConfigProvider.getConfig()
.getOptionalValue("nextcode.fts.resteasy.gzip.max-input", MemorySize::class.java)
if (ftsResteasyGzipMaxInput.isPresent) {
_maxInput = ftsResteasyGzipMaxInput.get()
} else {
_maxInput = MemorySize(BigInteger.valueOf(100 * 1024 * 1024)) // Default to 100M
}
}
return _maxInput!!.asLongValue()
}
@Throws(IOException::class)
override fun aroundReadFrom(context: ReaderInterceptorContext): Any {
if (!isEnabled) {
return context.proceed()
}
val clientAcceptEncodingHeaders = context.headers[HttpHeaders.ACCEPT_ENCODING]
val clientAcceptsGzip = clientAcceptEncodingHeaders?.flatMap { headerLine ->
headerLine.split(",").map { headerItem -> headerItem.trim() }
}?.contains("gzip") ?: false
if (clientAcceptsGzip) {
context.setProperty(COMPRESS_RESPONSE, true)
}
val requestContentEncoding = context.headers.getFirst(HttpHeaders.CONTENT_ENCODING)
if (requestContentEncoding?.contains("gzip") == true) {
context.inputStream = ObservableInputStream(GZIPInputStream(context.inputStream)) { bytesRead ->
if (bytesRead > maxInputBytes) {
// We can only do this after we've read the content as streams usually don't support reporting size
val size = if (maxInputBytes > Int.MAX_VALUE) Int.MAX_VALUE else maxInputBytes.toInt()
throw WebApplicationException(
Response.status(REQUEST_ENTITY_TOO_LARGE)
.entity(Messages.MESSAGES.gzipExceedsMaxSize(size)).build()
)
}
}
}
return context.proceed()
}
@Throws(IOException::class)
override fun aroundWriteTo(context: WriterInterceptorContext) {
if (!isEnabled) {
return context.proceed()
}
if (true == context.getProperty(COMPRESS_RESPONSE)) {
val headers = context.headers
headers.add(HttpHeaders.CONTENT_ENCODING, "gzip")
context.outputStream = GZIPOutputStream(context.outputStream)
}
context.proceed()
}
companion object {
const val COMPRESS_RESPONSE = "__COMPRESS_RESPONSE__"
}
}
/**
* An input stream that observes the amount of data streamed and reports it to a callback function.
*/
class ObservableInputStream(
private val wrapped: InputStream,
private val onBytesRead: (Long) -> Unit
) : InputStream() {
private var bytesRead: Long = 0
// Delegate all read methods of InputStream, collecting the bytes read.
@Throws(IOException::class)
override fun read(): Int {
val res = wrapped.read()
if (res > -1) {
bytesRead++
}
onBytesRead(bytesRead)
return res
}
@Throws(IOException::class)
override fun read(b: ByteArray): Int {
val res = wrapped.read(b)
if (res > -1) {
bytesRead += res
onBytesRead(bytesRead)
}
return res
}
@Throws(IOException::class)
override fun read(b: ByteArray, off: Int, len: Int): Int {
val res = wrapped.read(b, off, len)
if (res > -1) {
bytesRead += res
onBytesRead(bytesRead)
}
return res
}
// Delegate all other methods of InputStream directly
@Throws(IOException::class)
override fun close() {
wrapped.close()
}
@Throws(IOException::class)
override fun readAllBytes(): ByteArray {
return wrapped.readAllBytes()
}
@Throws(IOException::class)
override fun readNBytes(len: Int): ByteArray {
return wrapped.readNBytes(len)
}
@Throws(IOException::class)
override fun readNBytes(b: ByteArray, off: Int, len: Int): Int {
return wrapped.readNBytes(b, off, len)
}
@Throws(IOException::class)
override fun skip(n: Long): Long {
return wrapped.skip(n)
}
@Throws(IOException::class)
override fun skipNBytes(n: Long) {
wrapped.skipNBytes(n)
}
@Throws(IOException::class)
override fun available(): Int {
return wrapped.available()
}
override fun mark(readlimit: Int) {
wrapped.mark(readlimit)
}
@Throws(IOException::class)
override fun reset() {
wrapped.reset()
}
override fun markSupported(): Boolean {
return wrapped.markSupported()
}
@Throws(IOException::class)
override fun transferTo(out: OutputStream): Long {
return wrapped.transferTo(out)
}
}
> Task :compileKotlin
Caching disabled for task ':compileKotlin' because:
Build cache is disabled
Task ':compileKotlin' is not up-to-date because:
Task has failed previously.
The input changes require a full rebuild for incremental task ':compileKotlin'.
file or directory '/Users/stefan/work/gor/quarkus/file-transfer-service/src/main/java', not found
file or directory '/Users/stefan/work/gor/quarkus/file-transfer-service/src/main/java', not found
Using Kotlin/JVM incremental compilation
i: starting the daemon as: /Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home/bin/java -cp /Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-compiler-embeddable/1.3.72/fb72232c8fa977d5e07d33c43381ddbdc5edab6/kotlin-compiler-embeddable-1.3.72.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.3.72/86613e1a669a701b0c660bfd2af4f82a7ae11fca/kotlin-reflect-1.3.72.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.72/657d8d34d91e1964b4439378c09933e840bfe8d5/kotlin-script-runtime-1.3.72.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-daemon-embeddable/1.3.72/53bbaa2816dbf6bdb85f629451b1fcc88f2164d2/kotlin-daemon-embeddable-1.3.72.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.intellij.deps/trove4j/1.0.20181211/216c2e14b070f334479d800987affe4054cd563f/trove4j-1.0.20181211.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/Users/stefan/.m2/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar -Djava.awt.headless=true -Djava.rmi.server.hostname=127.0.0.1 -Xmx512m -XX:MaxMetaspaceSize=256m -Dkotlin.environment.keepalive -ea org.jetbrains.kotlin.daemon.KotlinCompileDaemon --daemon-runFilesPath /Users/stefan/Library/Application Support/kotlin/daemon --daemon-autoshutdownIdleSeconds=7200 --daemon-compilerClasspath /Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-compiler-embeddable/1.3.72/fb72232c8fa977d5e07d33c43381ddbdc5edab6/kotlin-compiler-embeddable-1.3.72.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.3.72/86613e1a669a701b0c660bfd2af4f82a7ae11fca/kotlin-reflect-1.3.72.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.72/657d8d34d91e1964b4439378c09933e840bfe8d5/kotlin-script-runtime-1.3.72.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-daemon-embeddable/1.3.72/53bbaa2816dbf6bdb85f629451b1fcc88f2164d2/kotlin-daemon-embeddable-1.3.72.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.intellij.deps/trove4j/1.0.20181211/216c2e14b070f334479d800987affe4054cd563f/trove4j-1.0.20181211.jar:/Users/stefan/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/Users/stefan/.m2/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar
i: Received the message signalling that the daemon is ready
i: new daemon started, trying to find it
i: found daemon on port 17766 (28 ms old), trying to connect
i: connected to the daemon
Options for KOTLIN DAEMON: IncrementalCompilationOptions(super=CompilationOptions(compilerMode=INCREMENTAL_COMPILER, targetPlatform=JVM, reportCategories=[0], reportSeverity=2, requestedCompilationResults=[0]kotlinScriptExtensions=[kt, kts, java]), areFileChangesKnown=false, modifiedFiles=null, deletedFiles=null, workingDir=/Users/stefan/work/gor/quarkus/file-transfer-service/build/kotlin/compileKotlin, multiModuleICSettings=MultiModuleICSettings(buildHistoryFile=/Users/stefan/work/gor/quarkus/file-transfer-service/build/kotlin/compileKotlin/build-history.bin, useModuleDetection=false), usePreciseJavaTracking=trueoutputFiles=[/Users/stefan/work/gor/quarkus/file-transfer-service/build/classes/kotlin/main, /Users/stefan/work/gor/quarkus/file-transfer-service/build/kotlin/compileKotlin])
e: /Users/stefan/work/gor/quarkus/file-transfer-service/src/main/kotlin/com/genuitysci/fileservice/interceptor/GZIPInterceptor.kt: (187, 5): 'skipNBytes' overrides nothing
e: /Users/stefan/work/gor/quarkus/file-transfer-service/src/main/kotlin/com/genuitysci/fileservice/interceptor/GZIPInterceptor.kt: (188, 15): Unresolved reference: skipNBytes
> Task :compileKotlin FAILED
:compileKotlin (Thread[Execution worker for ':',5,main]) completed. Took 7.144 secs.
FAILURE: Build failed with an exception.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment