Logging overhauls
This commit is contained in:
parent
45957ad922
commit
9c81c415cd
9
src/main/java/mdnet/cache/DiskLruCache.java
vendored
9
src/main/java/mdnet/cache/DiskLruCache.java
vendored
|
@ -17,8 +17,6 @@ package mdnet.cache;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
@ -94,8 +92,6 @@ import java.util.regex.Pattern;
|
||||||
* responding appropriately.
|
* responding appropriately.
|
||||||
*/
|
*/
|
||||||
public final class DiskLruCache implements Closeable {
|
public final class DiskLruCache implements Closeable {
|
||||||
private final static Logger LOGGER = LoggerFactory.getLogger(DiskLruCache.class);
|
|
||||||
|
|
||||||
private static final String JOURNAL_FILE = "journal";
|
private static final String JOURNAL_FILE = "journal";
|
||||||
private static final String JOURNAL_FILE_TEMP = "journal.tmp";
|
private static final String JOURNAL_FILE_TEMP = "journal.tmp";
|
||||||
private static final String JOURNAL_FILE_BACKUP = "journal.bkp";
|
private static final String JOURNAL_FILE_BACKUP = "journal.bkp";
|
||||||
|
@ -827,7 +823,6 @@ public final class DiskLruCache implements Closeable {
|
||||||
outputStream = new FileOutputStream(dirtyFile);
|
outputStream = new FileOutputStream(dirtyFile);
|
||||||
} catch (FileNotFoundException e2) {
|
} catch (FileNotFoundException e2) {
|
||||||
// We are unable to recover. Silently eat the writes.
|
// We are unable to recover. Silently eat the writes.
|
||||||
LOGGER.warn("Returning NULL_OUTPUT_STREAM", e2);
|
|
||||||
return NULL_OUTPUT_STREAM;
|
return NULL_OUTPUT_STREAM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -899,7 +894,6 @@ public final class DiskLruCache implements Closeable {
|
||||||
try {
|
try {
|
||||||
out.write(oneByte);
|
out.write(oneByte);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.warn("FaultHidingOutputStream exception in write()", e);
|
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -909,7 +903,6 @@ public final class DiskLruCache implements Closeable {
|
||||||
try {
|
try {
|
||||||
out.write(buffer, offset, length);
|
out.write(buffer, offset, length);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.warn("FaultHidingOutputStream exception in write()", e);
|
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -919,7 +912,6 @@ public final class DiskLruCache implements Closeable {
|
||||||
try {
|
try {
|
||||||
out.close();
|
out.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.warn("FaultHidingOutputStream exception in close()", e);
|
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -929,7 +921,6 @@ public final class DiskLruCache implements Closeable {
|
||||||
try {
|
try {
|
||||||
out.flush();
|
out.flush();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.warn("FaultHidingOutputStream exception in flush()", e);
|
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package mdnet.cache;
|
package mdnet.cache;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
|
@ -22,7 +22,6 @@ import java.time.Duration
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
const val CLIENT_BUILD = 14
|
const val CLIENT_BUILD = 14
|
||||||
const val WEBUI_VERSION = "0.1.1"
|
|
||||||
val MAX_AGE_CACHE: Duration = Duration.ofDays(14)
|
val MAX_AGE_CACHE: Duration = Duration.ofDays(14)
|
||||||
|
|
||||||
const val MAX_READ_TIME_SECONDS = 300
|
const val MAX_READ_TIME_SECONDS = 300
|
||||||
|
|
|
@ -85,26 +85,21 @@ object Main {
|
||||||
}
|
}
|
||||||
}.apply(::validateSettings)
|
}.apply(::validateSettings)
|
||||||
|
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Client settings loaded: $settings" }
|
||||||
LOGGER.info("Client settings loaded: {}", settings)
|
|
||||||
}
|
|
||||||
val client = MangaDexClient(settings)
|
val client = MangaDexClient(settings)
|
||||||
Runtime.getRuntime().addShutdownHook(Thread { client.shutdown() })
|
Runtime.getRuntime().addShutdownHook(Thread { client.shutdown() })
|
||||||
client.runLoop()
|
client.runLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dieWithError(e: Throwable): Nothing {
|
fun dieWithError(e: Throwable): Nothing {
|
||||||
if (LOGGER.isErrorEnabled) {
|
LOGGER.error(e) { "Critical Error" }
|
||||||
LOGGER.error("Critical Error", e)
|
|
||||||
}
|
|
||||||
(LoggerFactory.getILoggerFactory() as LoggerContext).stop()
|
(LoggerFactory.getILoggerFactory() as LoggerContext).stop()
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dieWithError(error: String): Nothing {
|
fun dieWithError(error: String): Nothing {
|
||||||
if (LOGGER.isErrorEnabled) {
|
LOGGER.error { "Critical Error: $error" }
|
||||||
LOGGER.error("Critical Error: {}", error)
|
|
||||||
}
|
|
||||||
(LoggerFactory.getILoggerFactory() as LoggerContext).stop()
|
(LoggerFactory.getILoggerFactory() as LoggerContext).stop()
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,10 +83,10 @@ class MangaDexClient(private val clientSettings: ClientSettings) {
|
||||||
statistics.set(JACKSON.readValue<Statistics>(it.getInputStream(0)))
|
statistics.set(JACKSON.readValue<Statistics>(it.getInputStream(0)))
|
||||||
}
|
}
|
||||||
} catch (e: HeaderMismatchException) {
|
} catch (e: HeaderMismatchException) {
|
||||||
LOGGER.warn("Cache version may be outdated - remove if necessary")
|
LOGGER.warn { "Cache version may be outdated - remove if necessary" }
|
||||||
dieWithError(e)
|
dieWithError(e)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
LOGGER.warn("Cache may be corrupt - remove if necessary")
|
LOGGER.warn { "Cache may be corrupt - remove if necessary" }
|
||||||
dieWithError(e)
|
dieWithError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,9 +99,7 @@ class MangaDexClient(private val clientSettings: ClientSettings) {
|
||||||
webUi = getUiServer(clientSettings.webSettings, statistics, statsMap)
|
webUi = getUiServer(clientSettings.webSettings, statistics, statsMap)
|
||||||
webUi!!.start()
|
webUi!!.start()
|
||||||
}
|
}
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Mangadex@Home Client initialized. Starting normal operation." }
|
||||||
LOGGER.info("Mangadex@Home Client initialized. Starting normal operation.")
|
|
||||||
}
|
|
||||||
|
|
||||||
executorService.scheduleAtFixedRate({
|
executorService.scheduleAtFixedRate({
|
||||||
try {
|
try {
|
||||||
|
@ -117,7 +115,7 @@ class MangaDexClient(private val clientSettings: ClientSettings) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
LOGGER.warn("Statistics update failed", e)
|
LOGGER.warn(e) { "Statistics update failed" }
|
||||||
}
|
}
|
||||||
}, 15, 15, TimeUnit.SECONDS)
|
}, 15, 15, TimeUnit.SECONDS)
|
||||||
|
|
||||||
|
@ -128,19 +126,17 @@ class MangaDexClient(private val clientSettings: ClientSettings) {
|
||||||
|
|
||||||
val state = this.state
|
val state = this.state
|
||||||
if (state is GracefulShutdown) {
|
if (state is GracefulShutdown) {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Aborting graceful shutdown started due to hourly bandwidth limit" }
|
||||||
LOGGER.info("Aborting graceful shutdown started due to hourly bandwidth limit")
|
|
||||||
}
|
|
||||||
this.state = state.lastRunning
|
this.state = state.lastRunning
|
||||||
}
|
}
|
||||||
if (state is Uninitialized) {
|
if (state is Uninitialized) {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Restarting server stopped due to hourly bandwidth limit" }
|
||||||
LOGGER.info("Restarting server stopped due to hourly bandwidth limit")
|
|
||||||
}
|
|
||||||
loginAndStartServer()
|
loginAndStartServer()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
LOGGER.warn("Hourly bandwidth check failed", e)
|
LOGGER.warn(e) { "Hourly bandwidth check failed" }
|
||||||
}
|
}
|
||||||
}, 1, 1, TimeUnit.HOURS)
|
}, 1, 1, TimeUnit.HOURS)
|
||||||
|
|
||||||
|
@ -151,31 +147,27 @@ class MangaDexClient(private val clientSettings: ClientSettings) {
|
||||||
if (state is GracefulShutdown) {
|
if (state is GracefulShutdown) {
|
||||||
when {
|
when {
|
||||||
state.counts == 0 -> {
|
state.counts == 0 -> {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Starting graceful shutdown" }
|
||||||
LOGGER.info("Starting graceful shutdown")
|
|
||||||
}
|
|
||||||
logout()
|
logout()
|
||||||
isHandled.set(false)
|
isHandled.set(false)
|
||||||
this.state = state.copy(counts = state.counts + 1)
|
this.state = state.copy(counts = state.counts + 1)
|
||||||
}
|
}
|
||||||
state.counts == timesToWait || !isHandled.get() -> {
|
state.counts == timesToWait || !isHandled.get() -> {
|
||||||
if (LOGGER.isInfoEnabled) {
|
if (!isHandled.get()) {
|
||||||
if (!isHandled.get()) {
|
LOGGER.info { "No requests received, shutting down" }
|
||||||
LOGGER.info("No requests received, shutting down")
|
} else {
|
||||||
} else {
|
LOGGER.info { "Max tries attempted (${state.counts} out of $timesToWait), shutting down" }
|
||||||
LOGGER.info("Max tries attempted (${state.counts} out of $timesToWait), shutting down")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stopServer(state.nextState)
|
stopServer(state.nextState)
|
||||||
state.action()
|
state.action()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info {
|
||||||
LOGGER.info(
|
"Waiting another 15 seconds for graceful shutdown (${state.counts} out of $timesToWait)"
|
||||||
"Waiting another 15 seconds for graceful shutdown (${state.counts} out of $timesToWait)"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isHandled.set(false)
|
isHandled.set(false)
|
||||||
this.state = state.copy(counts = state.counts + 1)
|
this.state = state.copy(counts = state.counts + 1)
|
||||||
}
|
}
|
||||||
|
@ -192,16 +184,15 @@ class MangaDexClient(private val clientSettings: ClientSettings) {
|
||||||
if (state is Running) {
|
if (state is Running) {
|
||||||
val currentBytesSent = statistics.get().bytesSent - lastBytesSent
|
val currentBytesSent = statistics.get().bytesSent - lastBytesSent
|
||||||
if (clientSettings.maxMebibytesPerHour != 0L && clientSettings.maxMebibytesPerHour * 1024 * 1024 /* MiB to bytes */ < currentBytesSent) {
|
if (clientSettings.maxMebibytesPerHour != 0L && clientSettings.maxMebibytesPerHour * 1024 * 1024 /* MiB to bytes */ < currentBytesSent) {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Shutting down server as hourly bandwidth limit reached" }
|
||||||
LOGGER.info("Shutting down server as hourly bandwidth limit reached")
|
|
||||||
}
|
|
||||||
this.state = GracefulShutdown(lastRunning = state)
|
this.state = GracefulShutdown(lastRunning = state)
|
||||||
} else {
|
} else {
|
||||||
pingControl()
|
pingControl()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
LOGGER.warn("Graceful shutdown checker failed", e)
|
LOGGER.warn(e) { "Graceful shutdown checker failed" }
|
||||||
}
|
}
|
||||||
}, 45, 45, TimeUnit.SECONDS)
|
}, 45, 45, TimeUnit.SECONDS)
|
||||||
}
|
}
|
||||||
|
@ -211,30 +202,23 @@ class MangaDexClient(private val clientSettings: ClientSettings) {
|
||||||
|
|
||||||
val newSettings = serverHandler.pingControl(state.settings)
|
val newSettings = serverHandler.pingControl(state.settings)
|
||||||
if (newSettings != null) {
|
if (newSettings != null) {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Server settings received: $newSettings" }
|
||||||
LOGGER.info("Server settings received: $newSettings")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newSettings.latestBuild > Constants.CLIENT_BUILD) {
|
if (newSettings.latestBuild > Constants.CLIENT_BUILD) {
|
||||||
if (LOGGER.isWarnEnabled) {
|
LOGGER.warn {
|
||||||
LOGGER.warn(
|
"Outdated build detected! Latest: ${newSettings.latestBuild}, Current: ${Constants.CLIENT_BUILD}"
|
||||||
"Outdated build detected! Latest: ${newSettings.latestBuild}, Current: ${Constants.CLIENT_BUILD}"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newSettings.tls != null || newSettings.imageServer != state.settings.imageServer) {
|
if (newSettings.tls != null || newSettings.imageServer != state.settings.imageServer) {
|
||||||
// certificates or upstream url must have changed, restart webserver
|
// certificates or upstream url must have changed, restart webserver
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Doing internal restart of HTTP server to refresh certs/upstream URL" }
|
||||||
LOGGER.info("Doing internal restart of HTTP server to refresh certs/upstream URL")
|
|
||||||
}
|
|
||||||
this.state = GracefulShutdown(lastRunning = state) {
|
this.state = GracefulShutdown(lastRunning = state) {
|
||||||
loginAndStartServer()
|
loginAndStartServer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Server ping failed - ignoring" }
|
||||||
LOGGER.info("Server ping failed - ignoring")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,17 +230,13 @@ class MangaDexClient(private val clientSettings: ClientSettings) {
|
||||||
val server = getServer(cache, serverSettings, clientSettings, statistics, isHandled).start()
|
val server = getServer(cache, serverSettings, clientSettings, statistics, isHandled).start()
|
||||||
|
|
||||||
if (serverSettings.latestBuild > Constants.CLIENT_BUILD) {
|
if (serverSettings.latestBuild > Constants.CLIENT_BUILD) {
|
||||||
if (LOGGER.isWarnEnabled) {
|
LOGGER.warn {
|
||||||
LOGGER.warn(
|
"Outdated build detected! Latest: ${serverSettings.latestBuild}, Current: ${Constants.CLIENT_BUILD}"
|
||||||
"Outdated build detected! Latest: ${serverSettings.latestBuild}, Current: ${Constants.CLIENT_BUILD}"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state = Running(server, serverSettings)
|
state = Running(server, serverSettings)
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Internal HTTP server was successfully started" }
|
||||||
LOGGER.info("Internal HTTP server was successfully started")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logout() {
|
private fun logout() {
|
||||||
|
@ -275,18 +255,15 @@ class MangaDexClient(private val clientSettings: ClientSettings) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Shutting down HTTP server" }
|
||||||
LOGGER.info("Shutting down HTTP server")
|
|
||||||
}
|
|
||||||
state.server.stop()
|
state.server.stop()
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Internal HTTP server has shut down" }
|
||||||
LOGGER.info("Internal HTTP server has shut down")
|
|
||||||
}
|
|
||||||
this.state = nextState
|
this.state = nextState
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shutdown() {
|
fun shutdown() {
|
||||||
LOGGER.info("Mangadex@Home Client stopping")
|
LOGGER.info { "Mangadex@Home Client stopping" }
|
||||||
|
|
||||||
val latch = CountDownLatch(1)
|
val latch = CountDownLatch(1)
|
||||||
executorService.schedule({
|
executorService.schedule({
|
||||||
|
@ -310,11 +287,11 @@ class MangaDexClient(private val clientSettings: ClientSettings) {
|
||||||
try {
|
try {
|
||||||
cache.close()
|
cache.close()
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
LOGGER.error("Cache failed to close", e)
|
LOGGER.error(e) { "Cache failed to close" }
|
||||||
}
|
}
|
||||||
|
|
||||||
executorService.shutdown()
|
executorService.shutdown()
|
||||||
LOGGER.info("Mangadex@Home Client stopped")
|
LOGGER.info { "Mangadex@Home Client stopped" }
|
||||||
|
|
||||||
(LoggerFactory.getILoggerFactory() as LoggerContext).stop()
|
(LoggerFactory.getILoggerFactory() as LoggerContext).stop()
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,9 +44,7 @@ class ServerHandler(private val settings: ClientSettings) {
|
||||||
private val client = ApacheClient()
|
private val client = ApacheClient()
|
||||||
|
|
||||||
fun logoutFromControl(): Boolean {
|
fun logoutFromControl(): Boolean {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Disconnecting from the control server" }
|
||||||
LOGGER.info("Disconnecting from the control server")
|
|
||||||
}
|
|
||||||
val params = mapOf<String, Any>(
|
val params = mapOf<String, Any>(
|
||||||
"secret" to settings.clientSecret
|
"secret" to settings.clientSecret
|
||||||
)
|
)
|
||||||
|
@ -79,9 +77,7 @@ class ServerHandler(private val settings: ClientSettings) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loginToControl(): ServerSettings? {
|
fun loginToControl(): ServerSettings? {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Connecting to the control server" }
|
||||||
LOGGER.info("Connecting to the control server")
|
|
||||||
}
|
|
||||||
|
|
||||||
val request = STRING_ANY_MAP_LENS(getPingParams(), Request(Method.POST, getServerAddress() + "ping"))
|
val request = STRING_ANY_MAP_LENS(getPingParams(), Request(Method.POST, getServerAddress() + "ping"))
|
||||||
val response = client(request)
|
val response = client(request)
|
||||||
|
@ -94,9 +90,7 @@ class ServerHandler(private val settings: ClientSettings) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pingControl(old: ServerSettings): ServerSettings? {
|
fun pingControl(old: ServerSettings): ServerSettings? {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Pinging the control server" }
|
||||||
LOGGER.info("Pinging the control server")
|
|
||||||
}
|
|
||||||
|
|
||||||
val request = STRING_ANY_MAP_LENS(getPingParams(old.tls!!.createdAt), Request(Method.POST, getServerAddress() + "ping"))
|
val request = STRING_ANY_MAP_LENS(getPingParams(old.tls!!.createdAt), Request(Method.POST, getServerAddress() + "ping"))
|
||||||
val response = client(request)
|
val response = client(request)
|
||||||
|
|
|
@ -1,3 +1,21 @@
|
||||||
|
/*
|
||||||
|
Mangadex@Home
|
||||||
|
Copyright (c) 2020, MangaDex Network
|
||||||
|
This file is part of MangaDex@Home.
|
||||||
|
|
||||||
|
MangaDex@Home is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
MangaDex@Home is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this MangaDex@Home. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
package mdnet.base.data
|
package mdnet.base.data
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy
|
import com.fasterxml.jackson.databind.PropertyNamingStrategy
|
||||||
|
|
54
src/main/kotlin/mdnet/base/logging.kt
Normal file
54
src/main/kotlin/mdnet/base/logging.kt
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package mdnet.base
|
||||||
|
|
||||||
|
import org.slf4j.Logger
|
||||||
|
|
||||||
|
inline fun Logger.error(msg: () -> String) {
|
||||||
|
if (this.isErrorEnabled) {
|
||||||
|
this.error(msg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline fun Logger.warn(msg: () -> String) {
|
||||||
|
if (this.isWarnEnabled) {
|
||||||
|
this.warn(msg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline fun Logger.info(msg: () -> String) {
|
||||||
|
if (this.isInfoEnabled) {
|
||||||
|
this.info(msg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline fun Logger.debug(msg: () -> String) {
|
||||||
|
if (this.isDebugEnabled) {
|
||||||
|
this.debug(msg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline fun Logger.trace(msg: () -> String) {
|
||||||
|
if (this.isTraceEnabled) {
|
||||||
|
this.trace(msg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline fun Logger.error(e: Throwable, msg: () -> String) {
|
||||||
|
if (this.isErrorEnabled) {
|
||||||
|
this.error(msg(), e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline fun Logger.warn(e: Throwable, msg: () -> String) {
|
||||||
|
if (this.isWarnEnabled) {
|
||||||
|
this.warn(msg(), e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline fun Logger.info(e: Throwable, msg: () -> String) {
|
||||||
|
if (this.isInfoEnabled) {
|
||||||
|
this.info(msg(), e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline fun Logger.debug(e: Throwable, msg: () -> String) {
|
||||||
|
if (this.isDebugEnabled) {
|
||||||
|
this.debug(msg(), e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline fun Logger.trace(e: Throwable, msg: () -> String) {
|
||||||
|
if (this.isTraceEnabled) {
|
||||||
|
this.trace(msg(), e)
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,8 +47,10 @@ import java.util.concurrent.atomic.AtomicReference
|
||||||
import javax.net.ssl.SSLException
|
import javax.net.ssl.SSLException
|
||||||
import mdnet.base.Constants
|
import mdnet.base.Constants
|
||||||
import mdnet.base.data.Statistics
|
import mdnet.base.data.Statistics
|
||||||
|
import mdnet.base.info
|
||||||
import mdnet.base.settings.ClientSettings
|
import mdnet.base.settings.ClientSettings
|
||||||
import mdnet.base.settings.TlsCert
|
import mdnet.base.settings.TlsCert
|
||||||
|
import mdnet.base.trace
|
||||||
import org.http4k.core.HttpHandler
|
import org.http4k.core.HttpHandler
|
||||||
import org.http4k.server.Http4kChannelHandler
|
import org.http4k.server.Http4kChannelHandler
|
||||||
import org.http4k.server.Http4kServer
|
import org.http4k.server.Http4kServer
|
||||||
|
@ -75,9 +77,7 @@ class Netty(private val tls: TlsCert, private val clientSettings: ClientSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun start(): Http4kServer = apply {
|
override fun start(): Http4kServer = apply {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Starting Netty with ${clientSettings.threads} threads" }
|
||||||
LOGGER.info("Starting Netty with {} threads", clientSettings.threads)
|
|
||||||
}
|
|
||||||
|
|
||||||
val certs = getX509Certs(tls.certificate)
|
val certs = getX509Certs(tls.certificate)
|
||||||
val sslContext = SslContextBuilder
|
val sslContext = SslContextBuilder
|
||||||
|
@ -107,16 +107,10 @@ class Netty(private val tls: TlsCert, private val clientSettings: ClientSettings
|
||||||
ch.pipeline().addLast("handle_ssl", object : ChannelInboundHandlerAdapter() {
|
ch.pipeline().addLast("handle_ssl", object : ChannelInboundHandlerAdapter() {
|
||||||
override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
|
override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
|
||||||
if (cause is SSLException || (cause is DecoderException && cause.cause is SSLException)) {
|
if (cause is SSLException || (cause is DecoderException && cause.cause is SSLException)) {
|
||||||
if (LOGGER.isTraceEnabled) {
|
LOGGER.trace { "Ignored invalid SSL connection" }
|
||||||
LOGGER.trace("Ignored invalid SSL connection")
|
|
||||||
}
|
|
||||||
} else if (cause is IOException || cause is SocketException) {
|
} else if (cause is IOException || cause is SocketException) {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "User (downloader) abruptly closed the connection" }
|
||||||
LOGGER.info("User (downloader) abruptly closed the connection")
|
LOGGER.trace(cause) { "Exception in pipeline" }
|
||||||
}
|
|
||||||
if (LOGGER.isTraceEnabled) {
|
|
||||||
LOGGER.trace("Exception in pipeline", cause)
|
|
||||||
}
|
|
||||||
} else if (cause !is ReadTimeoutException && cause !is WriteTimeoutException) {
|
} else if (cause !is ReadTimeoutException && cause !is WriteTimeoutException) {
|
||||||
ctx.fireExceptionCaught(cause)
|
ctx.fireExceptionCaught(cause)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,19 @@ along with this MangaDex@Home. If not, see <http://www.gnu.org/licenses/>.
|
||||||
/* ktlint-disable no-wildcard-imports */
|
/* ktlint-disable no-wildcard-imports */
|
||||||
package mdnet.base.server
|
package mdnet.base.server
|
||||||
|
|
||||||
|
import java.net.InetAddress
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import mdnet.base.data.Statistics
|
import mdnet.base.data.Statistics
|
||||||
|
import mdnet.base.info
|
||||||
import mdnet.base.netty.Netty
|
import mdnet.base.netty.Netty
|
||||||
import mdnet.base.settings.ClientSettings
|
import mdnet.base.settings.ClientSettings
|
||||||
import mdnet.base.settings.ServerSettings
|
import mdnet.base.settings.ServerSettings
|
||||||
import mdnet.cache.DiskLruCache
|
import mdnet.cache.DiskLruCache
|
||||||
|
import org.apache.http.client.config.CookieSpecs
|
||||||
|
import org.apache.http.client.config.RequestConfig
|
||||||
|
import org.apache.http.impl.client.HttpClients
|
||||||
|
import org.http4k.client.ApacheClient
|
||||||
import org.http4k.core.*
|
import org.http4k.core.*
|
||||||
import org.http4k.filter.ServerFilters
|
import org.http4k.filter.ServerFilters
|
||||||
import org.http4k.routing.bind
|
import org.http4k.routing.bind
|
||||||
|
@ -39,11 +45,30 @@ private val LOGGER = LoggerFactory.getLogger("Application")
|
||||||
|
|
||||||
fun getServer(cache: DiskLruCache, serverSettings: ServerSettings, clientSettings: ClientSettings, statistics: AtomicReference<Statistics>, isHandled: AtomicBoolean): Http4kServer {
|
fun getServer(cache: DiskLruCache, serverSettings: ServerSettings, clientSettings: ClientSettings, statistics: AtomicReference<Statistics>, isHandled: AtomicBoolean): Http4kServer {
|
||||||
val database = Database.connect("jdbc:sqlite:cache/data.db", "org.sqlite.JDBC")
|
val database = Database.connect("jdbc:sqlite:cache/data.db", "org.sqlite.JDBC")
|
||||||
val imageServer = ImageServer(cache, statistics, serverSettings, database, clientSettings.clientHostname, isHandled)
|
val client = ApacheClient(responseBodyMode = BodyMode.Stream, client = HttpClients.custom()
|
||||||
|
.disableConnectionState()
|
||||||
|
.setDefaultRequestConfig(
|
||||||
|
RequestConfig.custom()
|
||||||
|
.setCookieSpec(CookieSpecs.IGNORE_COOKIES)
|
||||||
|
.setConnectTimeout(3000)
|
||||||
|
.setSocketTimeout(3000)
|
||||||
|
.setConnectionRequestTimeout(3000)
|
||||||
|
.apply {
|
||||||
|
if (clientSettings.clientHostname != "0.0.0.0") {
|
||||||
|
setLocalAddress(InetAddress.getByName(clientSettings.clientHostname))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.build())
|
||||||
|
.setMaxConnTotal(3000)
|
||||||
|
.setMaxConnPerRoute(3000)
|
||||||
|
.build())
|
||||||
|
|
||||||
|
val imageServer = ImageServer(cache, database, statistics, serverSettings, client)
|
||||||
|
|
||||||
return timeRequest()
|
return timeRequest()
|
||||||
.then(catchAllHideDetails())
|
.then(catchAllHideDetails())
|
||||||
.then(ServerFilters.CatchLensFailure)
|
.then(ServerFilters.CatchLensFailure)
|
||||||
|
.then(setHandled(isHandled))
|
||||||
.then(addCommonHeaders())
|
.then(addCommonHeaders())
|
||||||
.then(
|
.then(
|
||||||
routes(
|
routes(
|
||||||
|
@ -62,6 +87,15 @@ fun getServer(cache: DiskLruCache, serverSettings: ServerSettings, clientSetting
|
||||||
.asServer(Netty(serverSettings.tls!!, clientSettings, statistics))
|
.asServer(Netty(serverSettings.tls!!, clientSettings, statistics))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setHandled(isHandled: AtomicBoolean): Filter {
|
||||||
|
return Filter { next: HttpHandler ->
|
||||||
|
{
|
||||||
|
isHandled.set(true)
|
||||||
|
next(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun timeRequest(): Filter {
|
fun timeRequest(): Filter {
|
||||||
return Filter { next: HttpHandler ->
|
return Filter { next: HttpHandler ->
|
||||||
{ request: Request ->
|
{ request: Request ->
|
||||||
|
@ -73,17 +107,14 @@ fun timeRequest(): Filter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Request for $cleanedUri received from ${request.source?.address}" }
|
||||||
LOGGER.info("Request for $cleanedUri received from ${request.source?.address}")
|
|
||||||
}
|
|
||||||
|
|
||||||
val start = System.currentTimeMillis()
|
val start = System.currentTimeMillis()
|
||||||
val response = next(request)
|
val response = next(request)
|
||||||
val latency = System.currentTimeMillis() - start
|
val latency = System.currentTimeMillis() - start
|
||||||
|
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Request for $cleanedUri completed (TTFB) in ${latency}ms" }
|
||||||
LOGGER.info("Request for $cleanedUri completed (TTFB) in ${latency}ms")
|
|
||||||
}
|
|
||||||
response.header("X-Time-Taken", latency.toString())
|
response.header("X-Time-Taken", latency.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,13 +32,11 @@ import java.io.BufferedInputStream
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.InetAddress
|
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.CipherInputStream
|
import javax.crypto.CipherInputStream
|
||||||
|
@ -49,13 +47,12 @@ import mdnet.base.data.ImageData
|
||||||
import mdnet.base.data.ImageDatum
|
import mdnet.base.data.ImageDatum
|
||||||
import mdnet.base.data.Statistics
|
import mdnet.base.data.Statistics
|
||||||
import mdnet.base.data.Token
|
import mdnet.base.data.Token
|
||||||
|
import mdnet.base.info
|
||||||
import mdnet.base.settings.ServerSettings
|
import mdnet.base.settings.ServerSettings
|
||||||
|
import mdnet.base.trace
|
||||||
|
import mdnet.base.warn
|
||||||
import mdnet.cache.CachingInputStream
|
import mdnet.cache.CachingInputStream
|
||||||
import mdnet.cache.DiskLruCache
|
import mdnet.cache.DiskLruCache
|
||||||
import org.apache.http.client.config.CookieSpecs
|
|
||||||
import org.apache.http.client.config.RequestConfig
|
|
||||||
import org.apache.http.impl.client.HttpClients
|
|
||||||
import org.http4k.client.ApacheClient
|
|
||||||
import org.http4k.core.*
|
import org.http4k.core.*
|
||||||
import org.http4k.filter.CachingFilters
|
import org.http4k.filter.CachingFilters
|
||||||
import org.http4k.filter.CorsPolicy
|
import org.http4k.filter.CorsPolicy
|
||||||
|
@ -66,32 +63,19 @@ import org.jetbrains.exposed.sql.SchemaUtils
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
private const val THREADS_TO_ALLOCATE = 262144 // 2**18
|
class ImageServer(
|
||||||
|
private val cache: DiskLruCache,
|
||||||
class ImageServer(private val cache: DiskLruCache, private val statistics: AtomicReference<Statistics>, private val serverSettings: ServerSettings, private val database: Database, private val clientHostname: String, private val handled: AtomicBoolean) {
|
private val database: Database,
|
||||||
|
private val statistics: AtomicReference<Statistics>,
|
||||||
|
private val serverSettings: ServerSettings,
|
||||||
|
private val client: HttpHandler
|
||||||
|
) {
|
||||||
init {
|
init {
|
||||||
transaction(database) {
|
transaction(database) {
|
||||||
SchemaUtils.create(ImageData)
|
SchemaUtils.create(ImageData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private val executor = Executors.newCachedThreadPool()
|
private val executor = Executors.newCachedThreadPool()
|
||||||
private val client = ApacheClient(responseBodyMode = BodyMode.Stream, client = HttpClients.custom()
|
|
||||||
.disableConnectionState()
|
|
||||||
.setDefaultRequestConfig(
|
|
||||||
RequestConfig.custom()
|
|
||||||
.setCookieSpec(CookieSpecs.IGNORE_COOKIES)
|
|
||||||
.setConnectTimeout(3000)
|
|
||||||
.setSocketTimeout(3000)
|
|
||||||
.setConnectionRequestTimeout(3000)
|
|
||||||
.apply {
|
|
||||||
if (clientHostname != "0.0.0.0") {
|
|
||||||
setLocalAddress(InetAddress.getByName(clientHostname))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.build())
|
|
||||||
.setMaxConnTotal(THREADS_TO_ALLOCATE)
|
|
||||||
.setMaxConnPerRoute(THREADS_TO_ALLOCATE)
|
|
||||||
.build())
|
|
||||||
|
|
||||||
fun handler(dataSaver: Boolean, tokenized: Boolean = false): HttpHandler {
|
fun handler(dataSaver: Boolean, tokenized: Boolean = false): HttpHandler {
|
||||||
val sodium = LazySodiumJava(SodiumJava())
|
val sodium = LazySodiumJava(SodiumJava())
|
||||||
|
@ -115,30 +99,22 @@ class ImageServer(private val cache: DiskLruCache, private val statistics: Atomi
|
||||||
tokenArr.sliceArray(24 until tokenArr.size), tokenArr.sliceArray(0 until 24), serverSettings.tokenKey
|
tokenArr.sliceArray(24 until tokenArr.size), tokenArr.sliceArray(0 until 24), serverSettings.tokenKey
|
||||||
)
|
)
|
||||||
} catch (_: SodiumException) {
|
} catch (_: SodiumException) {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Request for $sanitizedUri rejected for invalid token" }
|
||||||
LOGGER.info("Request for $sanitizedUri rejected for invalid token")
|
|
||||||
}
|
|
||||||
return@then Response(Status.FORBIDDEN)
|
return@then Response(Status.FORBIDDEN)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} catch (e: JsonProcessingException) {
|
} catch (e: JsonProcessingException) {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Request for $sanitizedUri rejected for invalid token" }
|
||||||
LOGGER.info("Request for $sanitizedUri rejected for invalid token")
|
|
||||||
}
|
|
||||||
return@then Response(Status.FORBIDDEN)
|
return@then Response(Status.FORBIDDEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OffsetDateTime.now().isAfter(token.expires)) {
|
if (OffsetDateTime.now().isAfter(token.expires)) {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Request for $sanitizedUri rejected for expired token" }
|
||||||
LOGGER.info("Request for $sanitizedUri rejected for expired token")
|
|
||||||
}
|
|
||||||
return@then Response(Status.GONE)
|
return@then Response(Status.GONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.hash != chapterHash) {
|
if (token.hash != chapterHash) {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Request for $sanitizedUri rejected for inapplicable token" }
|
||||||
LOGGER.info("Request for $sanitizedUri rejected for inapplicable token")
|
|
||||||
}
|
|
||||||
return@then Response(Status.FORBIDDEN)
|
return@then Response(Status.FORBIDDEN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +137,6 @@ class ImageServer(private val cache: DiskLruCache, private val statistics: Atomi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handled.set(true)
|
|
||||||
if (request.header("Referer")?.startsWith("https://mangadex.org") == false) {
|
if (request.header("Referer")?.startsWith("https://mangadex.org") == false) {
|
||||||
snapshot?.close()
|
snapshot?.close()
|
||||||
Response(Status.FORBIDDEN)
|
Response(Status.FORBIDDEN)
|
||||||
|
@ -170,10 +145,7 @@ class ImageServer(private val cache: DiskLruCache, private val statistics: Atomi
|
||||||
} else {
|
} else {
|
||||||
if (snapshot != null) {
|
if (snapshot != null) {
|
||||||
snapshot.close()
|
snapshot.close()
|
||||||
|
LOGGER.warn { "Removing cache file for $sanitizedUri without corresponding DB entry" }
|
||||||
if (LOGGER.isWarnEnabled) {
|
|
||||||
LOGGER.warn("Removing cache file for $sanitizedUri without corresponding DB entry")
|
|
||||||
}
|
|
||||||
cache.removeUnsafe(imageId.toCacheId())
|
cache.removeUnsafe(imageId.toCacheId())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,9 +161,7 @@ class ImageServer(private val cache: DiskLruCache, private val statistics: Atomi
|
||||||
it.copy(browserCached = it.browserCached + 1)
|
it.copy(browserCached = it.browserCached + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Request for $sanitizedUri cached by browser" }
|
||||||
LOGGER.info("Request for $sanitizedUri cached by browser")
|
|
||||||
}
|
|
||||||
|
|
||||||
val lastModified = imageDatum.lastModified
|
val lastModified = imageDatum.lastModified
|
||||||
snapshot.close()
|
snapshot.close()
|
||||||
|
@ -203,9 +173,7 @@ class ImageServer(private val cache: DiskLruCache, private val statistics: Atomi
|
||||||
it.copy(cacheHits = it.cacheHits + 1)
|
it.copy(cacheHits = it.cacheHits + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Request for $sanitizedUri hit cache" }
|
||||||
LOGGER.info("Request for $sanitizedUri hit cache")
|
|
||||||
}
|
|
||||||
|
|
||||||
respondWithImage(
|
respondWithImage(
|
||||||
CipherInputStream(BufferedInputStream(snapshot.getInputStream(0)), cipher),
|
CipherInputStream(BufferedInputStream(snapshot.getInputStream(0)), cipher),
|
||||||
|
@ -216,9 +184,8 @@ class ImageServer(private val cache: DiskLruCache, private val statistics: Atomi
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Request.handleCacheMiss(sanitizedUri: String, cipher: Cipher, imageId: String, imageDatum: ImageDatum?): Response {
|
private fun Request.handleCacheMiss(sanitizedUri: String, cipher: Cipher, imageId: String, imageDatum: ImageDatum?): Response {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Request for $sanitizedUri missed cache" }
|
||||||
LOGGER.info("Request for $sanitizedUri missed cache")
|
|
||||||
}
|
|
||||||
statistics.getAndUpdate {
|
statistics.getAndUpdate {
|
||||||
it.copy(cacheMisses = it.cacheMisses + 1)
|
it.copy(cacheMisses = it.cacheMisses + 1)
|
||||||
}
|
}
|
||||||
|
@ -226,16 +193,13 @@ class ImageServer(private val cache: DiskLruCache, private val statistics: Atomi
|
||||||
val mdResponse = client(Request(Method.GET, "${serverSettings.imageServer}$sanitizedUri"))
|
val mdResponse = client(Request(Method.GET, "${serverSettings.imageServer}$sanitizedUri"))
|
||||||
|
|
||||||
if (mdResponse.status != Status.OK) {
|
if (mdResponse.status != Status.OK) {
|
||||||
if (LOGGER.isTraceEnabled) {
|
LOGGER.trace { "Upstream query for $sanitizedUri errored with status ${mdResponse.status}" }
|
||||||
LOGGER.trace("Upstream query for $sanitizedUri errored with status {}", mdResponse.status)
|
|
||||||
}
|
|
||||||
mdResponse.close()
|
mdResponse.close()
|
||||||
return Response(mdResponse.status)
|
return Response(mdResponse.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOGGER.isTraceEnabled) {
|
LOGGER.trace { "Upstream query for $sanitizedUri succeeded" }
|
||||||
LOGGER.trace("Upstream query for $sanitizedUri succeeded")
|
|
||||||
}
|
|
||||||
|
|
||||||
val contentType = mdResponse.header("Content-Type")!!
|
val contentType = mdResponse.header("Content-Type")!!
|
||||||
val contentLength = mdResponse.header("Content-Length")
|
val contentLength = mdResponse.header("Content-Length")
|
||||||
|
@ -246,9 +210,7 @@ class ImageServer(private val cache: DiskLruCache, private val statistics: Atomi
|
||||||
// A null editor means that this file is being written to
|
// A null editor means that this file is being written to
|
||||||
// concurrently so we skip the cache process
|
// concurrently so we skip the cache process
|
||||||
return if (editor != null && contentLength != null && lastModified != null) {
|
return if (editor != null && contentLength != null && lastModified != null) {
|
||||||
if (LOGGER.isTraceEnabled) {
|
LOGGER.trace { "Request for $sanitizedUri is being cached and served" }
|
||||||
LOGGER.trace("Request for $sanitizedUri is being cached and served")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageDatum == null) {
|
if (imageDatum == null) {
|
||||||
synchronized(database) {
|
synchronized(database) {
|
||||||
|
@ -267,29 +229,20 @@ class ImageServer(private val cache: DiskLruCache, private val statistics: Atomi
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
if (editor.getLength(0) == contentLength.toLong()) {
|
if (editor.getLength(0) == contentLength.toLong()) {
|
||||||
if (LOGGER.isInfoEnabled) {
|
LOGGER.info { "Cache download for $sanitizedUri committed" }
|
||||||
LOGGER.info("Cache download for $sanitizedUri committed")
|
|
||||||
}
|
|
||||||
editor.commit()
|
editor.commit()
|
||||||
} else {
|
} else {
|
||||||
if (LOGGER.isWarnEnabled) {
|
LOGGER.warn { "Cache download for $sanitizedUri aborted" }
|
||||||
LOGGER.warn("Cache download for $sanitizedUri aborted")
|
|
||||||
}
|
|
||||||
editor.abort()
|
editor.abort()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (LOGGER.isWarnEnabled) {
|
LOGGER.warn(e) { "Cache go/no go for $sanitizedUri failed" }
|
||||||
LOGGER.warn("Cache go/no go for $sanitizedUri failed", e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
respondWithImage(tee, contentLength, contentType, lastModified, false)
|
respondWithImage(tee, contentLength, contentType, lastModified, false)
|
||||||
} else {
|
} else {
|
||||||
editor?.abort()
|
editor?.abort()
|
||||||
|
LOGGER.trace { "Request for $sanitizedUri is being served" }
|
||||||
if (LOGGER.isTraceEnabled) {
|
|
||||||
LOGGER.trace("Request for $sanitizedUri is being served")
|
|
||||||
}
|
|
||||||
respondWithImage(mdResponse.body.stream, contentLength, contentType, lastModified, false)
|
respondWithImage(mdResponse.body.stream, contentLength, contentType, lastModified, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,13 +280,13 @@ class ImageServer(private val cache: DiskLruCache, private val statistics: Atomi
|
||||||
private fun baseHandler(): Filter =
|
private fun baseHandler(): Filter =
|
||||||
CachingFilters.Response.MaxAge(Clock.systemUTC(), Constants.MAX_AGE_CACHE)
|
CachingFilters.Response.MaxAge(Clock.systemUTC(), Constants.MAX_AGE_CACHE)
|
||||||
.then(ServerFilters.Cors(
|
.then(ServerFilters.Cors(
|
||||||
CorsPolicy(
|
CorsPolicy(
|
||||||
origins = listOf("https://mangadex.org"),
|
origins = listOf("https://mangadex.org"),
|
||||||
headers = listOf("*"),
|
headers = listOf("*"),
|
||||||
methods = Method.values().toList()
|
methods = Method.values().toList()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
.then(Filter { next: HttpHandler ->
|
.then(Filter { next: HttpHandler ->
|
||||||
{ request: Request ->
|
{ request: Request ->
|
||||||
val response = next(request)
|
val response = next(request)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.time.format.DateTimeFormatter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import mdnet.BuildInfo
|
import mdnet.BuildInfo
|
||||||
import mdnet.base.Constants
|
import mdnet.base.Constants
|
||||||
|
import mdnet.base.warn
|
||||||
import org.http4k.core.Filter
|
import org.http4k.core.Filter
|
||||||
import org.http4k.core.HttpHandler
|
import org.http4k.core.HttpHandler
|
||||||
import org.http4k.core.Request
|
import org.http4k.core.Request
|
||||||
|
@ -51,9 +52,8 @@ fun catchAllHideDetails(): Filter {
|
||||||
try {
|
try {
|
||||||
next(request)
|
next(request)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (LOGGER.isWarnEnabled) {
|
LOGGER.warn(e) { "Request error detected" }
|
||||||
LOGGER.warn("Request error detected", e)
|
|
||||||
}
|
|
||||||
Response(Status.INTERNAL_SERVER_ERROR)
|
Response(Status.INTERNAL_SERVER_ERROR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue