Add http proxy options

This commit is contained in:
Vankka 2024-12-11 22:38:35 +02:00
parent 50b16d69ea
commit d211b19278
No known key found for this signature in database
GPG Key ID: 62E48025ED4E7EBB
4 changed files with 147 additions and 23 deletions

View File

@ -24,16 +24,17 @@ import com.discordsrv.api.events.lifecycle.DiscordSRVReloadedEvent;
import com.discordsrv.api.events.lifecycle.DiscordSRVShuttingDownEvent;
import com.discordsrv.api.module.Module;
import com.discordsrv.api.reload.ReloadFlag;
import com.discordsrv.api.reload.ReloadResult;
import com.discordsrv.common.abstraction.bootstrap.IBootstrap;
import com.discordsrv.common.command.discord.DiscordCommandModule;
import com.discordsrv.common.command.game.GameCommandModule;
import com.discordsrv.api.reload.ReloadResult;
import com.discordsrv.common.config.configurate.manager.ConnectionConfigManager;
import com.discordsrv.common.config.configurate.manager.MainConfigManager;
import com.discordsrv.common.config.configurate.manager.MessagesConfigManager;
import com.discordsrv.common.config.configurate.manager.MessagesConfigSingleManager;
import com.discordsrv.common.config.connection.BotConfig;
import com.discordsrv.common.config.connection.ConnectionConfig;
import com.discordsrv.common.config.connection.HttpProxyConfig;
import com.discordsrv.common.config.connection.UpdateConfig;
import com.discordsrv.common.config.main.MainConfig;
import com.discordsrv.common.config.main.linking.LinkedAccountConfig;
@ -102,10 +103,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.UnknownHostException;
import java.net.*;
import java.nio.file.Path;
import java.time.Duration;
import java.time.ZonedDateTime;
@ -210,15 +208,19 @@ public abstract class AbstractDiscordSRV<
logger.warning("");
///////////////////////////////////////////////////////////////
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(20); // Set maximum amount of requests at a time (to something more reasonable than 64)
dispatcher.setMaxRequestsPerHost(16); // Most requests are to discord.com
createHttpClient();
}
ConnectionPool connectionPool = new ConnectionPool(5, 10, TimeUnit.SECONDS);
private HttpProxyConfig usedProxyConfig = null;
private void createHttpClient() {
HttpProxyConfig proxyConfig = connectionConfig() != null ? connectionConfig().httpProxy : null;
if (httpClient != null && Objects.equals(usedProxyConfig, proxyConfig)) {
// Skip recreating client, if proxy is the same
return;
}
usedProxyConfig = proxyConfig;
this.httpClient = new OkHttpClient.Builder()
.dispatcher(dispatcher)
.connectionPool(connectionPool)
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(chain -> {
Request original = chain.request();
String host = original.url().host();
@ -238,8 +240,41 @@ public abstract class AbstractDiscordSRV<
})
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.build();
.writeTimeout(20, TimeUnit.SECONDS);
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(20); // Set maximum amount of requests at a time (to something more reasonable than 64)
dispatcher.setMaxRequestsPerHost(16); // Most requests are to discord.com
builder.dispatcher(dispatcher);
builder.connectionPool(new ConnectionPool(5, 10, TimeUnit.SECONDS));
if (proxyConfig != null && proxyConfig.enabled) {
builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyConfig.host, proxyConfig.port)));
HttpProxyConfig.BasicAuthConfig basicAuthConfig = proxyConfig.basicAuth;
if (basicAuthConfig != null && basicAuthConfig.enabled) {
// https://stackoverflow.com/questions/41806422/java-web-start-unable-to-tunnel-through-proxy-since-java-8-update-111
String disabledSchemes = "jdk.http.auth.tunneling.disabledSchemes";
String oldDisabledSchemes = System.getProperty(disabledSchemes);
if (oldDisabledSchemes != null) {
String newDisabledSchemes = Arrays.stream(oldDisabledSchemes.split(","))
.filter(disabledScheme -> !disabledScheme.equalsIgnoreCase("Basic"))
.collect(Collectors.joining(","));
if (!oldDisabledSchemes.equals(newDisabledSchemes)) {
System.setProperty(disabledSchemes, newDisabledSchemes);
}
}
String credential = Credentials.basic(basicAuthConfig.username, basicAuthConfig.password);
builder.proxyAuthenticator((route, response) -> response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build());
}
}
this.httpClient = builder.build();
}
protected URL getManifest() {
@ -711,6 +746,7 @@ public abstract class AbstractDiscordSRV<
messagesConfigManager().load();
channelConfig().reload();
createHttpClient();
} catch (Throwable t) {
if (initial) {
setStatus(Status.FAILED_TO_LOAD_CONFIG);
@ -847,7 +883,7 @@ public abstract class AbstractDiscordSRV<
// Modules are reloaded upon DiscordSRV being ready, thus not needed at initial
if (!initial && flags.contains(ReloadFlag.CONFIG)) {
results.addAll(moduleManager.reload());
results.addAll(moduleManager().reload());
}
if (flags.contains(ReloadFlag.DISCORD_COMMANDS)) {
@ -893,14 +929,16 @@ public abstract class AbstractDiscordSRV<
eventBus().shutdown();
// Shutdown OkHttp
httpClient.dispatcher().executorService().shutdownNow();
httpClient.connectionPool().evictAll();
try {
Cache cache = httpClient.cache();
if (cache != null) {
cache.close();
}
} catch (IOException ignored) {}
if (httpClient != null) {
httpClient.dispatcher().executorService().shutdownNow();
httpClient.connectionPool().evictAll();
try {
Cache cache = httpClient.cache();
if (cache != null) {
cache.close();
}
} catch (IOException ignored) {}
}
try {
if (storage != null) {

View File

@ -49,4 +49,6 @@ public class ConnectionConfig implements Config {
public MinecraftAuthConfig minecraftAuth = new MinecraftAuthConfig();
public UpdateConfig update = new UpdateConfig();
public HttpProxyConfig httpProxy = new HttpProxyConfig();
}

View File

@ -0,0 +1,69 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2024 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program 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.
*
* This program 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 program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.config.connection;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import java.util.Objects;
@ConfigSerializable
public class HttpProxyConfig {
public boolean enabled = false;
public String host = "example.com";
public int port = 8080;
public BasicAuthConfig basicAuth = new BasicAuthConfig();
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
HttpProxyConfig that = (HttpProxyConfig) o;
return enabled == that.enabled
&& port == that.port
&& Objects.equals(host, that.host)
&& Objects.equals(basicAuth, that.basicAuth);
}
@Override
public int hashCode() {
return Objects.hash(enabled, host, port, basicAuth);
}
@ConfigSerializable
public static class BasicAuthConfig {
public boolean enabled = true;
public String username = "discordsrv";
public String password = "";
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
BasicAuthConfig that = (BasicAuthConfig) o;
return enabled == that.enabled
&& Objects.equals(username, that.username)
&& Objects.equals(password, that.password);
}
@Override
public int hashCode() {
return Objects.hash(enabled, username, password);
}
}
}

View File

@ -33,6 +33,7 @@ import com.discordsrv.api.placeholder.PlaceholderLookupResult;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.connection.BotConfig;
import com.discordsrv.common.config.connection.ConnectionConfig;
import com.discordsrv.common.config.connection.HttpProxyConfig;
import com.discordsrv.common.config.documentation.DocumentationURLs;
import com.discordsrv.common.config.main.MemberCachingConfig;
import com.discordsrv.common.core.logging.Logger;
@ -46,6 +47,7 @@ import com.discordsrv.common.discord.connection.details.DiscordConnectionDetails
import com.discordsrv.common.feature.debug.DebugGenerateEvent;
import com.discordsrv.common.feature.debug.file.TextDebugFile;
import com.discordsrv.common.helper.Timeout;
import com.neovisionaries.ws.client.ProxySettings;
import com.neovisionaries.ws.client.WebSocketFactory;
import com.neovisionaries.ws.client.WebSocketFrame;
import net.dv8tion.jda.api.JDA;
@ -407,6 +409,19 @@ public class JDAConnectionManager implements DiscordConnectionManager {
jdaBuilder.setHttpClient(discordSRV.httpClient());
WebSocketFactory webSocketFactory = new WebSocketFactory();
HttpProxyConfig proxyConfig = discordSRV.connectionConfig().httpProxy;
if (proxyConfig != null && proxyConfig.enabled) {
ProxySettings proxySettings = webSocketFactory.getProxySettings();
proxySettings.setHost(proxyConfig.host);
proxySettings.setPort(proxyConfig.port);
HttpProxyConfig.BasicAuthConfig basicAuthConfig = proxyConfig.basicAuth;
if (basicAuthConfig != null && basicAuthConfig.enabled) {
proxySettings.setCredentials(basicAuthConfig.username, basicAuthConfig.password);
}
}
jdaBuilder.setWebsocketFactory(webSocketFactory);
try {