1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-24 10:27:38 +00:00

Add support for proxying HTTP requests (#1461)

This commit is contained in:
Drew Edwards
2023-06-06 19:58:24 +01:00
committed by GitHub
parent d45f2bfe80
commit c91bb5ac33
19 changed files with 251 additions and 25 deletions

View File

@@ -45,6 +45,9 @@ class AddressRuleConfig {
config.setComment("max_websocket_message", "The maximum size (in bytes) that a computer can send or receive in one websocket packet.");
config.set("max_websocket_message", AddressRule.WEBSOCKET_MESSAGE);
config.setComment("use_proxy", "Enable use of the HTTP/SOCKS proxy if it is configured.");
config.set("use_proxy", false);
}
return config;
@@ -58,6 +61,7 @@ class AddressRuleConfig {
&& check(builder, "max_upload", Number.class)
&& check(builder, "max_download", Number.class)
&& check(builder, "websocket_message", Number.class)
&& check(builder, "use_proxy", Boolean.class)
&& AddressRule.parse(hostObj, port, PartialOptions.DEFAULT) != null;
}
@@ -71,12 +75,14 @@ class AddressRuleConfig {
var maxUpload = unboxOptLong(get(builder, "max_upload", Number.class).map(Number::longValue));
var maxDownload = unboxOptLong(get(builder, "max_download", Number.class).map(Number::longValue));
var websocketMessage = unboxOptInt(get(builder, "websocket_message", Number.class).map(Number::intValue));
var useProxy = get(builder, "use_proxy", Boolean.class);
var options = new PartialOptions(
action,
maxUpload,
maxDownload,
websocketMessage
websocketMessage,
useProxy
);
return AddressRule.parse(hostObj, port, options);

View File

@@ -8,6 +8,7 @@ import com.google.common.base.Splitter;
import javax.annotation.Nullable;
import javax.annotation.OverridingMethodsMustInvokeSuper;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
@@ -141,6 +142,18 @@ public interface ConfigFile {
* @param onChange The function to run on change.
* @return The built config file.
*/
public abstract ConfigFile build(Runnable onChange);
public abstract ConfigFile build(ConfigListener onChange);
}
@FunctionalInterface
interface ConfigListener {
/**
* The function called then a config file is changed.
*
* @param path The path to the config file. This will be {@code null} when the config file does not exist on
* disk, such as when synced from a server to the client.
* @see Builder#build(ConfigListener)
*/
void onConfigChanged(@Nullable Path path);
}
}

View File

@@ -5,10 +5,12 @@
package dan200.computercraft.shared.config;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.core.CoreConfig;
import dan200.computercraft.core.Logging;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.core.apis.http.options.ProxyType;
import dan200.computercraft.core.computer.mainthread.MainThreadConfig;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import dan200.computercraft.shared.platform.PlatformHelper;
@@ -16,6 +18,8 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.filter.MarkerFilter;
import javax.annotation.Nullable;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -49,6 +53,10 @@ public final class ConfigSpec {
public static final ConfigFile.Value<Integer> httpDownloadBandwidth;
public static final ConfigFile.Value<Integer> httpUploadBandwidth;
public static final ConfigFile.Value<ProxyType> httpProxyType;
public static final ConfigFile.Value<String> httpProxyHost;
public static final ConfigFile.Value<Integer> httpProxyPort;
public static final ConfigFile.Value<Boolean> commandBlockEnabled;
public static final ConfigFile.Value<Integer> modemRange;
public static final ConfigFile.Value<Integer> modemHighAltitudeRange;
@@ -222,6 +230,30 @@ public final class ConfigSpec {
builder.pop();
builder
.comment("""
Tunnels HTTP and websocket requests through a proxy server. Only affects HTTP
rules with "use_proxy" set to true (off by default).
If authentication is required for the proxy, create a "computercraft-proxy.pw"
file in the same directory as "computercraft-server.toml", containing the
username and password separated by a colon, e.g. "myuser:mypassword". For
SOCKS4 proxies only the username is required.""")
.push("proxy");
httpProxyType = builder
.comment("The type of proxy to use.")
.defineEnum("type", CoreConfig.httpProxyType);
httpProxyHost = builder
.comment("The hostname or IP address of the proxy server.")
.define("host", CoreConfig.httpProxyHost);
httpProxyPort = builder
.comment("The port of the proxy server.")
.defineInRange("port", CoreConfig.httpProxyPort, 1, 65536);
builder.pop();
builder.pop();
}
@@ -339,7 +371,7 @@ public final class ConfigSpec {
clientSpec = clientBuilder.build(ConfigSpec::syncClient);
}
public static void syncServer() {
public static void syncServer(@Nullable Path path) {
// General
Config.computerSpaceLimit = computerSpaceLimit.get();
Config.floppySpaceLimit = floppySpaceLimit.get();
@@ -370,6 +402,13 @@ public final class ConfigSpec {
CoreConfig.httpMaxWebsockets = httpMaxWebsockets.get();
CoreConfig.httpDownloadBandwidth = httpDownloadBandwidth.get();
CoreConfig.httpUploadBandwidth = httpUploadBandwidth.get();
CoreConfig.httpProxyType = httpProxyType.get();
CoreConfig.httpProxyHost = httpProxyHost.get();
CoreConfig.httpProxyPort = httpProxyPort.get();
if (path != null) ProxyPasswordConfig.init(path.resolveSibling(ComputerCraftAPI.MOD_ID + "-proxy.pw"));
NetworkUtils.reloadConfig();
// Peripheral
@@ -396,7 +435,7 @@ public final class ConfigSpec {
Config.monitorHeight = monitorHeight.get();
}
public static void syncClient() {
public static void syncClient(@Nullable Path path) {
Config.monitorRenderer = monitorRenderer.get();
Config.monitorDistance = monitorDistance.get();
Config.uploadNagDelay = uploadNagDelay.get();

View File

@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.config;
import dan200.computercraft.core.CoreConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
record ProxyPasswordConfig(String username, String password) {
private static final Logger LOG = LoggerFactory.getLogger(ProxyPasswordConfig.class);
@Nullable
private static ProxyPasswordConfig loadFromFile(@Nullable Path path) {
if (path == null || !path.toFile().exists()) return null;
try (var br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
var line = br.readLine();
if (line == null) return null;
var parts = line.trim().split(":", 2);
if (parts.length == 0) return null;
return new ProxyPasswordConfig(parts[0], parts.length == 2 ? parts[1] : "");
} catch (IOException e) {
LOG.error("Failed to load proxy password from {}.", path, e);
return null;
}
}
static void init(@Nullable Path path) {
var config = loadFromFile(path);
if (config == null) {
CoreConfig.httpProxyUsername = "";
CoreConfig.httpProxyPassword = "";
} else {
CoreConfig.httpProxyUsername = config.username;
CoreConfig.httpProxyPassword = config.password;
}
}
}