Rc4 release
- use openssl to reduce memory footprint - invalidate everyone's caches again (in order to fix 128 bit SSL on older JREs) - Add more logging - Hide secret printouts - Rebrand
This commit is contained in:
parent
a92ee85dd7
commit
d9fb96c08d
20
build.gradle
20
build.gradle
|
@ -1,14 +1,14 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id "java"
|
||||||
id 'org.jetbrains.kotlin.jvm' version '1.3.72'
|
id "org.jetbrains.kotlin.jvm" version "1.3.72"
|
||||||
id 'application'
|
id "application"
|
||||||
id 'com.github.johnrengelman.shadow' version '5.2.0'
|
id "com.github.johnrengelman.shadow" version "5.2.0"
|
||||||
id "com.diffplug.gradle.spotless" version "3.18.0"
|
id "com.diffplug.gradle.spotless" version "3.18.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = 'com.mangadex'
|
group = "com.mangadex"
|
||||||
version = '1.0.0-rc3'
|
version = "1.0.0-rc4"
|
||||||
mainClassName = 'mdnet.base.MangadexClient'
|
mainClassName = "mdnet.base.MangaDexClient"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -16,16 +16,16 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation group: 'com.konghq', name: 'unirest-java', version: '3.7.02'
|
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
|
implementation group: "com.konghq", name: "unirest-java", version: "3.7.02"
|
||||||
|
|
||||||
implementation group: "org.http4k", name: "http4k-core", version: "$http_4k_version"
|
implementation group: "org.http4k", name: "http4k-core", version: "$http_4k_version"
|
||||||
implementation group: "org.http4k", name: "http4k-server-netty", version: "$http_4k_version"
|
implementation group: "org.http4k", name: "http4k-server-netty", version: "$http_4k_version"
|
||||||
implementation group: "org.http4k", name: "http4k-client-apache", version: "$http_4k_version"
|
implementation group: "org.http4k", name: "http4k-client-apache", version: "$http_4k_version"
|
||||||
implementation group: 'commons-io', name: 'commons-io', version: '2.7'
|
implementation group: "commons-io", name: "commons-io", version: "2.7"
|
||||||
|
|
||||||
implementation "ch.qos.logback:logback-classic:$logback_version"
|
implementation "ch.qos.logback:logback-classic:$logback_version"
|
||||||
|
runtimeOnly 'io.netty:netty-tcnative-boringssl-static:2.0.30.Final'
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
|
|
|
@ -50,7 +50,7 @@ public final class ClientSettings {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ClientSettings{" + "maxCacheSizeMib=" + maxCacheSizeMib + ", maxBandwidthMibPerHour="
|
return "ClientSettings{" + "maxCacheSizeMib=" + maxCacheSizeMib + ", maxBandwidthMibPerHour="
|
||||||
+ maxBandwidthMibPerHour + ", maxBurstRateKibPerSecond=" + maxBurstRateKibPerSecond + ", clientPort="
|
+ maxBandwidthMibPerHour + ", maxBurstRateKibPerSecond=" + maxBurstRateKibPerSecond + ", clientPort="
|
||||||
+ clientPort + ", clientSecret='" + clientSecret + '\'' + '}';
|
+ clientPort + ", clientSecret='" + "<hidden>" + '\'' + '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSecretValid(String clientSecret) {
|
public static boolean isSecretValid(String clientSecret) {
|
||||||
|
|
|
@ -16,8 +16,8 @@ import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
public class MangadexClient {
|
public class MangaDexClient {
|
||||||
private final static Logger LOGGER = LoggerFactory.getLogger(MangadexClient.class);
|
private final static Logger LOGGER = LoggerFactory.getLogger(MangaDexClient.class);
|
||||||
|
|
||||||
// This lock protects the Http4kServer from concurrent restart attempts
|
// This lock protects the Http4kServer from concurrent restart attempts
|
||||||
private final Object shutdownLock = new Object();
|
private final Object shutdownLock = new Object();
|
||||||
|
@ -31,16 +31,16 @@ public class MangadexClient {
|
||||||
private Http4kServer engine;
|
private Http4kServer engine;
|
||||||
private DiskLruCache cache;
|
private DiskLruCache cache;
|
||||||
|
|
||||||
public MangadexClient(ClientSettings clientSettings) {
|
public MangaDexClient(ClientSettings clientSettings) {
|
||||||
this.clientSettings = clientSettings;
|
this.clientSettings = clientSettings;
|
||||||
this.serverHandler = new ServerHandler(clientSettings);
|
this.serverHandler = new ServerHandler(clientSettings);
|
||||||
this.statistics = new AtomicReference<>();
|
this.statistics = new AtomicReference<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cache = DiskLruCache.open(new File("cache"), 2, 3,
|
cache = DiskLruCache.open(new File("cache"), 3, 3,
|
||||||
clientSettings.getMaxCacheSizeMib() * 1024 * 1024 /* MiB to bytes */);
|
clientSettings.getMaxCacheSizeMib() * 1024 * 1024 /* MiB to bytes */);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
MangadexClient.dieWithError(e);
|
MangaDexClient.dieWithError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,10 +73,11 @@ public class MangadexClient {
|
||||||
}
|
}
|
||||||
statistics.set(new Statistics());
|
statistics.set(new Statistics());
|
||||||
|
|
||||||
|
if (engine == null) {
|
||||||
if (LOGGER.isInfoEnabled()) {
|
if (LOGGER.isInfoEnabled()) {
|
||||||
LOGGER.info("Restarting server stopped due to hourly bandwidth limit");
|
LOGGER.info("Restarting server stopped due to hourly bandwidth limit");
|
||||||
}
|
}
|
||||||
if (engine == null) {
|
|
||||||
loginAndStartServer();
|
loginAndStartServer();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -132,7 +133,7 @@ public class MangadexClient {
|
||||||
private void loginAndStartServer() {
|
private void loginAndStartServer() {
|
||||||
serverSettings = serverHandler.loginToControl();
|
serverSettings = serverHandler.loginToControl();
|
||||||
if (serverSettings == null) {
|
if (serverSettings == null) {
|
||||||
MangadexClient.dieWithError("Failed to get a login response from server - check API secret for validity");
|
MangaDexClient.dieWithError("Failed to get a login response from server - check API secret for validity");
|
||||||
}
|
}
|
||||||
engine = ApplicationKt.getServer(cache, serverSettings, clientSettings, statistics);
|
engine = ApplicationKt.getServer(cache, serverSettings, clientSettings, statistics);
|
||||||
engine.start();
|
engine.start();
|
||||||
|
@ -168,38 +169,38 @@ public class MangadexClient {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
System.out.println("Mangadex@Home Client " + Constants.CLIENT_VERSION + " (Build " + Constants.CLIENT_BUILD
|
System.out.println("Mangadex@Home Client " + Constants.CLIENT_VERSION + " (Build " + Constants.CLIENT_BUILD
|
||||||
+ ") initializing\n");
|
+ ") initializing\n");
|
||||||
System.out.println("Copyright (c) 2020, Mangadex");
|
System.out.println("Copyright (c) 2020, MangaDex Network");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String file = "settings.json";
|
String file = "settings.json";
|
||||||
if (args.length == 1) {
|
if (args.length == 1) {
|
||||||
file = args[0];
|
file = args[0];
|
||||||
} else if (args.length != 0) {
|
} else if (args.length != 0) {
|
||||||
MangadexClient.dieWithError("Expected one argument: path to config file, or nothing");
|
MangaDexClient.dieWithError("Expected one argument: path to config file, or nothing");
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientSettings settings = new Gson().fromJson(new FileReader(file), ClientSettings.class);
|
ClientSettings settings = new Gson().fromJson(new FileReader(file), ClientSettings.class);
|
||||||
|
|
||||||
if (!ClientSettings.isSecretValid(settings.getClientSecret()))
|
if (!ClientSettings.isSecretValid(settings.getClientSecret()))
|
||||||
MangadexClient.dieWithError("Config Error: API Secret is invalid, must be 52 alphanumeric characters");
|
MangaDexClient.dieWithError("Config Error: API Secret is invalid, must be 52 alphanumeric characters");
|
||||||
|
|
||||||
if (settings.getClientPort() == 0) {
|
if (settings.getClientPort() == 0) {
|
||||||
MangadexClient.dieWithError("Config Error: Invalid port number");
|
MangaDexClient.dieWithError("Config Error: Invalid port number");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.getMaxCacheSizeMib() < 1024) {
|
if (settings.getMaxCacheSizeMib() < 1024) {
|
||||||
MangadexClient.dieWithError("Config Error: Invalid max cache size, must be >= 1024 MiB (1GiB)");
|
MangaDexClient.dieWithError("Config Error: Invalid max cache size, must be >= 1024 MiB (1GiB)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOGGER.isInfoEnabled()) {
|
if (LOGGER.isInfoEnabled()) {
|
||||||
LOGGER.info("Client settings loaded: {}", settings);
|
LOGGER.info("Client settings loaded: {}", settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
MangadexClient client = new MangadexClient(settings);
|
MangaDexClient client = new MangaDexClient(settings);
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(client::shutdown));
|
Runtime.getRuntime().addShutdownHook(new Thread(client::shutdown));
|
||||||
client.runLoop();
|
client.runLoop();
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
MangadexClient.dieWithError(e);
|
MangaDexClient.dieWithError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,8 +83,8 @@ public final class ServerSettings {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "TlsCert{" + "createdAt='" + createdAt + '\'' + ", privateKey='" + privateKey + '\''
|
return "TlsCert{" + "createdAt='" + createdAt + '\'' + ", privateKey='" + "<hidden>" + '\''
|
||||||
+ ", certificate='" + certificate + '\'' + '}';
|
+ ", certificate='" + "<hidden>" + '\'' + '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
44
src/main/java/mdnet/cache/DiskLruCache.java
vendored
44
src/main/java/mdnet/cache/DiskLruCache.java
vendored
|
@ -244,8 +244,7 @@ public final class DiskLruCache implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readJournal() throws IOException {
|
private void readJournal() throws IOException {
|
||||||
StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), StandardCharsets.UTF_8);
|
try (StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), StandardCharsets.UTF_8)) {
|
||||||
try {
|
|
||||||
String magic = reader.readLine();
|
String magic = reader.readLine();
|
||||||
String version = reader.readLine();
|
String version = reader.readLine();
|
||||||
String appVersionString = reader.readLine();
|
String appVersionString = reader.readLine();
|
||||||
|
@ -276,8 +275,6 @@ public final class DiskLruCache implements Closeable {
|
||||||
journalWriter = new BufferedWriter(
|
journalWriter = new BufferedWriter(
|
||||||
new OutputStreamWriter(new FileOutputStream(journalFile, true), StandardCharsets.UTF_8));
|
new OutputStreamWriter(new FileOutputStream(journalFile, true), StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
Util.closeQuietly(reader);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,9 +349,8 @@ public final class DiskLruCache implements Closeable {
|
||||||
journalWriter.close();
|
journalWriter.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Writer writer = new BufferedWriter(
|
try (Writer writer = new BufferedWriter(
|
||||||
new OutputStreamWriter(new FileOutputStream(journalFileTmp), StandardCharsets.UTF_8));
|
new OutputStreamWriter(new FileOutputStream(journalFileTmp), StandardCharsets.UTF_8))) {
|
||||||
try {
|
|
||||||
writer.write(MAGIC);
|
writer.write(MAGIC);
|
||||||
writer.write("\n");
|
writer.write("\n");
|
||||||
writer.write(VERSION_1);
|
writer.write(VERSION_1);
|
||||||
|
@ -372,8 +368,6 @@ public final class DiskLruCache implements Closeable {
|
||||||
writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
|
writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
Util.closeQuietly(writer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (journalFile.exists()) {
|
if (journalFile.exists()) {
|
||||||
|
@ -430,7 +424,10 @@ public final class DiskLruCache implements Closeable {
|
||||||
// A file must have been deleted manually!
|
// A file must have been deleted manually!
|
||||||
for (int i = 0; i < valueCount; i++) {
|
for (int i = 0; i < valueCount; i++) {
|
||||||
if (ins[i] != null) {
|
if (ins[i] != null) {
|
||||||
Util.closeQuietly(ins[i]);
|
try {
|
||||||
|
ins[i].close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -698,11 +695,8 @@ public final class DiskLruCache implements Closeable {
|
||||||
* Returns the string value for {@code index}. This consumes the InputStream!
|
* Returns the string value for {@code index}. This consumes the InputStream!
|
||||||
*/
|
*/
|
||||||
public String getString(int index) throws IOException {
|
public String getString(int index) throws IOException {
|
||||||
InputStream in = getInputStream(index);
|
try (InputStream in = getInputStream(index)) {
|
||||||
try {
|
|
||||||
return IOUtils.toString(in, StandardCharsets.UTF_8);
|
return IOUtils.toString(in, StandardCharsets.UTF_8);
|
||||||
} finally {
|
|
||||||
Util.closeQuietly(in);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,7 +707,10 @@ public final class DiskLruCache implements Closeable {
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
for (InputStream in : ins) {
|
for (InputStream in : ins) {
|
||||||
Util.closeQuietly(in);
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -741,7 +738,7 @@ public final class DiskLruCache implements Closeable {
|
||||||
* Returns an unbuffered input stream to read the last committed value, or null
|
* Returns an unbuffered input stream to read the last committed value, or null
|
||||||
* if no value has been committed.
|
* if no value has been committed.
|
||||||
*/
|
*/
|
||||||
public InputStream newInputStream(int index) throws IOException {
|
public InputStream newInputStream(int index) {
|
||||||
synchronized (DiskLruCache.this) {
|
synchronized (DiskLruCache.this) {
|
||||||
if (entry.currentEditor != this) {
|
if (entry.currentEditor != this) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
@ -762,11 +759,8 @@ public final class DiskLruCache implements Closeable {
|
||||||
* committed.
|
* committed.
|
||||||
*/
|
*/
|
||||||
public String getString(int index) throws IOException {
|
public String getString(int index) throws IOException {
|
||||||
InputStream in = newInputStream(index);
|
try (InputStream in = newInputStream(index)) {
|
||||||
try {
|
|
||||||
return in != null ? IOUtils.toString(in, StandardCharsets.UTF_8) : null;
|
return in != null ? IOUtils.toString(in, StandardCharsets.UTF_8) : null;
|
||||||
} finally {
|
|
||||||
Util.closeQuietly(in);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,11 +768,8 @@ public final class DiskLruCache implements Closeable {
|
||||||
* Write a string to the specified index.
|
* Write a string to the specified index.
|
||||||
*/
|
*/
|
||||||
public void setString(int index, String value) throws IOException {
|
public void setString(int index, String value) throws IOException {
|
||||||
OutputStream out = newOutputStream(index);
|
try (OutputStream out = newOutputStream(index)) {
|
||||||
try {
|
|
||||||
IOUtils.write(value, out, StandardCharsets.UTF_8);
|
IOUtils.write(value, out, StandardCharsets.UTF_8);
|
||||||
} finally {
|
|
||||||
Util.closeQuietly(out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -811,6 +802,7 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -863,6 +855,7 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -872,6 +865,7 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -881,6 +875,7 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -890,6 +885,7 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
src/main/java/mdnet/cache/Util.java
vendored
12
src/main/java/mdnet/cache/Util.java
vendored
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package mdnet.cache;
|
package mdnet.cache;
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -43,15 +42,4 @@ final class Util {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void closeQuietly(/* Auto */Closeable closeable) {
|
|
||||||
if (closeable != null) {
|
|
||||||
try {
|
|
||||||
closeable.close();
|
|
||||||
} catch (RuntimeException rethrown) {
|
|
||||||
throw rethrown;
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,11 +47,18 @@ fun getServer(cache: DiskLruCache, serverSettings: ServerSettings, clientSetting
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
|
|
||||||
val app = { request: Request ->
|
val app = { dataSaver: Boolean ->
|
||||||
|
{ request: Request ->
|
||||||
|
|
||||||
val chapterHash = Path.of("chapterHash")(request)
|
val chapterHash = Path.of("chapterHash")(request)
|
||||||
val fileName = Path.of("fileName")(request)
|
val fileName = Path.of("fileName")(request)
|
||||||
val cacheId = md5String("$chapterHash.$fileName")
|
|
||||||
|
val rc4Bytes = if (dataSaver) {
|
||||||
|
md5Bytes("saver$chapterHash.$fileName")
|
||||||
|
} else {
|
||||||
|
md5Bytes("$chapterHash.$fileName")
|
||||||
|
}
|
||||||
|
val cacheId = printHexString(rc4Bytes)
|
||||||
|
|
||||||
statistics.get().requestsServed.incrementAndGet()
|
statistics.get().requestsServed.incrementAndGet()
|
||||||
|
|
||||||
|
@ -61,7 +68,10 @@ fun getServer(cache: DiskLruCache, serverSettings: ServerSettings, clientSetting
|
||||||
.header("Content-Type", type)
|
.header("Content-Type", type)
|
||||||
.header("X-Content-Type-Options", "nosniff")
|
.header("X-Content-Type-Options", "nosniff")
|
||||||
.header("Last-Modified", lastModified)
|
.header("Last-Modified", lastModified)
|
||||||
.header("Cache-Control", listOf("public", MaxAgeTtl(Constants.MAX_AGE_CACHE).toHeaderValue()).joinToString(", "))
|
.header(
|
||||||
|
"Cache-Control",
|
||||||
|
listOf("public", MaxAgeTtl(Constants.MAX_AGE_CACHE).toHeaderValue()).joinToString(", ")
|
||||||
|
)
|
||||||
.header("Timing-Allow-Origin", "https://mangadex.org")
|
.header("Timing-Allow-Origin", "https://mangadex.org")
|
||||||
.body(input, length.toLong())
|
.body(input, length.toLong())
|
||||||
|
|
||||||
|
@ -85,8 +95,10 @@ fun getServer(cache: DiskLruCache, serverSettings: ServerSettings, clientSetting
|
||||||
LOGGER.trace("Request for $chapterHash/$fileName hit cache")
|
LOGGER.trace("Request for $chapterHash/$fileName hit cache")
|
||||||
}
|
}
|
||||||
|
|
||||||
respondWithImage(CipherInputStream(snapshot.getInputStream(0), getRc4(cacheId)),
|
respondWithImage(
|
||||||
snapshot.getLength(0).toString(), snapshot.getString(1), snapshot.getString(2))
|
CipherInputStream(snapshot.getInputStream(0), getRc4(rc4Bytes)),
|
||||||
|
snapshot.getLength(0).toString(), snapshot.getString(1), snapshot.getString(2)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
statistics.get().cacheMisses.incrementAndGet()
|
statistics.get().cacheMisses.incrementAndGet()
|
||||||
|
@ -105,6 +117,9 @@ fun getServer(cache: DiskLruCache, serverSettings: ServerSettings, clientSetting
|
||||||
val contentLength = mdResponse.header("Content-Length")!!
|
val contentLength = mdResponse.header("Content-Length")!!
|
||||||
val contentType = mdResponse.header("Content-Type")!!
|
val contentType = mdResponse.header("Content-Type")!!
|
||||||
|
|
||||||
|
if (LOGGER.isTraceEnabled) {
|
||||||
|
LOGGER.trace("Grabbing DiskLruCache editor instance")
|
||||||
|
}
|
||||||
val editor = cache.edit(cacheId)
|
val editor = cache.edit(cacheId)
|
||||||
|
|
||||||
val lastModified = mdResponse.header("Last-Modified")!!
|
val lastModified = mdResponse.header("Last-Modified")!!
|
||||||
|
@ -118,8 +133,10 @@ fun getServer(cache: DiskLruCache, serverSettings: ServerSettings, clientSetting
|
||||||
editor.setString(1, contentType)
|
editor.setString(1, contentType)
|
||||||
editor.setString(2, lastModified)
|
editor.setString(2, lastModified)
|
||||||
|
|
||||||
val tee = CachingInputStream(mdResponse.body.stream,
|
val tee = CachingInputStream(
|
||||||
executor, CipherOutputStream(editor.newOutputStream(0), getRc4(cacheId))) {
|
mdResponse.body.stream,
|
||||||
|
executor, CipherOutputStream(editor.newOutputStream(0), getRc4(rc4Bytes))
|
||||||
|
) {
|
||||||
// Note: if neither of the options get called/are in the log
|
// Note: if neither of the options get called/are in the log
|
||||||
// check that tee gets closed and for exceptions in this lambda
|
// check that tee gets closed and for exceptions in this lambda
|
||||||
if (editor.getLength(0) == contentLength.toLong()) {
|
if (editor.getLength(0) == contentLength.toLong()) {
|
||||||
|
@ -147,6 +164,7 @@ fun getServer(cache: DiskLruCache, serverSettings: ServerSettings, clientSetting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CachingFilters
|
CachingFilters
|
||||||
|
|
||||||
|
@ -155,15 +173,16 @@ fun getServer(cache: DiskLruCache, serverSettings: ServerSettings, clientSetting
|
||||||
.then(addCommonHeaders())
|
.then(addCommonHeaders())
|
||||||
.then(
|
.then(
|
||||||
routes(
|
routes(
|
||||||
"/data/{chapterHash}/{fileName}" bind Method.GET to app
|
"/data/{chapterHash}/{fileName}" bind Method.GET to app(false)
|
||||||
|
// "/data-saver/{chapterHash}/{fileName}" bind Method.GET to app(true)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.asServer(Netty(serverSettings.tls, clientSettings, statistics))
|
.asServer(Netty(serverSettings.tls, clientSettings, statistics))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRc4(key: String): Cipher {
|
private fun getRc4(key: ByteArray): Cipher {
|
||||||
val rc4 = Cipher.getInstance("RC4")
|
val rc4 = Cipher.getInstance("RC4")
|
||||||
rc4.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key.toByteArray(), "RC4"))
|
rc4.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "RC4"))
|
||||||
return rc4
|
return rc4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,17 +204,23 @@ private fun catchAllHideDetails(): Filter {
|
||||||
try {
|
try {
|
||||||
next(request)
|
next(request)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
if (LOGGER.isWarnEnabled) {
|
||||||
|
LOGGER.warn("Request error detected", e)
|
||||||
|
}
|
||||||
Response(Status.INTERNAL_SERVER_ERROR)
|
Response(Status.INTERNAL_SERVER_ERROR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun md5String(stringToHash: String): String {
|
private fun md5Bytes(stringToHash: String): ByteArray {
|
||||||
val digest = MessageDigest.getInstance("MD5")
|
val digest = MessageDigest.getInstance("MD5")
|
||||||
|
return digest.digest(stringToHash.toByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun printHexString(bytes: ByteArray): String {
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
for (b in digest.digest(stringToHash.toByteArray())) {
|
for (b in bytes) {
|
||||||
sb.append(String.format("%02x", b))
|
sb.append(String.format("%02x", b))
|
||||||
}
|
}
|
||||||
return sb.toString()
|
return sb.toString()
|
||||||
|
|
Loading…
Reference in a new issue