This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 42733d37869f CAMEL-23911: camel-jbang - TUI support configuring HTTP
proxy settings
42733d37869f is described below
commit 42733d37869f2d00dc00a8fcdf812ab6cc7612bc
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Jul 26 08:12:18 2026 +0200
CAMEL-23911: camel-jbang - TUI support configuring HTTP proxy settings
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../dsl/jbang/core/commands/tui/DocHelper.java | 27 ++++++++----
.../dsl/jbang/core/commands/tui/OsvClient.java | 1 +
.../dsl/jbang/core/commands/tui/SettingsPopup.java | 38 ++++++++++++++---
.../dsl/jbang/core/commands/tui/TuiSettings.java | 48 ++++++++++++++++++++++
.../jbang/core/commands/tui/SettingsPopupTest.java | 14 ++++---
5 files changed, 107 insertions(+), 21 deletions(-)
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DocHelper.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DocHelper.java
index 53335ecdd091..823b94ae3a99 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DocHelper.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DocHelper.java
@@ -18,9 +18,12 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
import java.io.IOException;
import java.io.InputStream;
-import java.net.HttpURLConnection;
import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
+import java.time.Duration;
import org.apache.camel.dsl.jbang.core.common.ExampleHelper;
@@ -42,17 +45,23 @@ final class DocHelper {
static String downloadContent(String url) {
try {
- HttpURLConnection conn = (HttpURLConnection)
URI.create(url).toURL().openConnection();
- conn.setConnectTimeout(5000);
- conn.setReadTimeout(10000);
- try (InputStream is = conn.getInputStream()) {
- return new String(is.readAllBytes(), StandardCharsets.UTF_8);
- } finally {
- conn.disconnect();
+ HttpClient client = HttpClient.newBuilder()
+ .connectTimeout(Duration.ofSeconds(5))
+ .proxy(TuiSettings.proxySelector())
+ .build();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .timeout(Duration.ofSeconds(10))
+ .GET()
+ .build();
+ HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
+ if (response.statusCode() == 200) {
+ return response.body();
}
} catch (Exception e) {
- return null;
+ // ignore
}
+ return null;
}
static String asciidocToMarkdown(String adoc) {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OsvClient.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OsvClient.java
index 112e77597412..12ce01ce1837 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OsvClient.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OsvClient.java
@@ -94,6 +94,7 @@ class OsvClient {
try {
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
+ .proxy(TuiSettings.proxySelector())
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BATCH_URL))
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopup.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopup.java
index 0ea39ed5da30..bf0bbe231cb9 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopup.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopup.java
@@ -52,12 +52,14 @@ class SettingsPopup {
private static final int ROW_LOG_PIN = 3;
private static final int ROW_RATE_PER = 4;
private static final int ROW_FOLDER = 5;
- private static final int ROW_SHELL_HISTORY = 6;
- private static final int ROW_AI_PROVIDER = 7;
- private static final int ROW_AI_MODEL = 8;
- private static final int ROW_AI_URL = 9;
- private static final int ROW_AI_PROMPT_HISTORY = 10;
- private static final int ROW_COUNT = 11;
+ private static final int ROW_PROXY_HOST = 6;
+ private static final int ROW_PROXY_PORT = 7;
+ private static final int ROW_SHELL_HISTORY = 8;
+ private static final int ROW_AI_PROVIDER = 9;
+ private static final int ROW_AI_MODEL = 10;
+ private static final int ROW_AI_URL = 11;
+ private static final int ROW_AI_PROMPT_HISTORY = 12;
+ private static final int ROW_COUNT = 13;
private static final String[] LOG_PIN_OPTIONS = { "off", "25", "50", "75"
};
private static final String[] RATE_PER_OPTIONS = { "seconds", "minutes" };
@@ -83,6 +85,8 @@ class SettingsPopup {
private int ratePerIndex;
private int aiProviderIndex;
private TextInputState folderInput;
+ private TextInputState proxyHostInput;
+ private TextInputState proxyPortInput;
private TextInputState shellHistoryInput;
private TextInputState aiModelInput;
private TextInputState aiUrlInput;
@@ -155,6 +159,8 @@ class SettingsPopup {
ratePerIndex = "minutes".equals(currentRatePer) ? 1 : 0;
folderInput = new TextInputState(settings.getDefaultFolder() != null ?
settings.getDefaultFolder() : "");
+ proxyHostInput = new TextInputState(settings.getProxyHost() != null ?
settings.getProxyHost() : "");
+ proxyPortInput = new TextInputState(settings.getProxyPort() != null ?
settings.getProxyPort() : "");
shellHistoryInput = new TextInputState(settings.getShellHistory() !=
null ? settings.getShellHistory() : "");
String currentProvider = settings.getAiProvider() != null ?
settings.getAiProvider() : "auto";
int providerIdx = AI_PROVIDERS.indexOf(currentProvider);
@@ -251,6 +257,14 @@ class SettingsPopup {
handleTextInput(ke, folderInput);
return true;
}
+ if (selectedRow == ROW_PROXY_HOST) {
+ handleTextInput(ke, proxyHostInput);
+ return true;
+ }
+ if (selectedRow == ROW_PROXY_PORT) {
+ handleTextInput(ke, proxyPortInput);
+ return true;
+ }
if (selectedRow == ROW_SHELL_HISTORY) {
handleTextInput(ke, shellHistoryInput);
return true;
@@ -296,6 +310,8 @@ class SettingsPopup {
monitorContext.ratePerMinute = "minutes".equals(ratePerValue);
}
settings.setDefaultFolder(stripControlChars(folderInput.text().trim()));
+ settings.setProxyHost(stripControlChars(proxyHostInput.text().trim()));
+ settings.setProxyPort(stripControlChars(proxyPortInput.text().trim()));
settings.setShellHistory(stripControlChars(shellHistoryInput.text().trim()));
settings.setAiProvider(AI_PROVIDERS.get(aiProviderIndex));
settings.setAiModel(stripControlChars(aiModelInput.text().trim()));
@@ -361,6 +377,16 @@ class SettingsPopup {
renderFolder(frame, innerX + labelW, rowY, fieldW, selectedRow ==
ROW_FOLDER);
rowY++;
+ renderLabel(frame, innerX, rowY, labelW, "Proxy Host:", selectedRow ==
ROW_PROXY_HOST);
+ renderTextInput(frame, innerX + labelW, rowY, fieldW, proxyHostInput,
+ selectedRow == ROW_PROXY_HOST, "proxy.corp.com");
+ rowY++;
+
+ renderLabel(frame, innerX, rowY, labelW, "Proxy Port:", selectedRow ==
ROW_PROXY_PORT);
+ renderTextInput(frame, innerX + labelW, rowY, fieldW, proxyPortInput,
+ selectedRow == ROW_PROXY_PORT, "(8080)");
+ rowY++;
+
renderLabel(frame, innerX, rowY, labelW, "Shell History:", selectedRow
== ROW_SHELL_HISTORY);
renderTextInput(frame, innerX + labelW, rowY, fieldW,
shellHistoryInput,
selectedRow == ROW_SHELL_HISTORY, "(100)");
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiSettings.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiSettings.java
index ad5d07ceeca3..d586473ce3fc 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiSettings.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiSettings.java
@@ -16,6 +16,9 @@
*/
package org.apache.camel.dsl.jbang.core.commands.tui;
+import java.net.InetSocketAddress;
+import java.net.ProxySelector;
+
/**
* User-facing preferences for the Camel TUI, persisted under {@code
camel.tui.*} keys in the CLI user configuration
* files. The object is loaded as a whole ({@link #load()}), mutated in
memory, and written back ({@link #save()}),
@@ -36,6 +39,8 @@ final class TuiSettings {
static final String PROP_AI_PROVIDER = "camel.tui.ai.provider";
static final String PROP_AI_MODEL = "camel.tui.ai.model";
static final String PROP_AI_URL = "camel.tui.ai.url";
+ static final String PROP_PROXY_HOST = "camel.tui.proxyHost";
+ static final String PROP_PROXY_PORT = "camel.tui.proxyPort";
static final String PROP_SHELL_HISTORY = "camel.tui.shell.history";
static final String PROP_AI_PROMPT_HISTORY = "camel.tui.ai.promptHistory";
@@ -45,6 +50,8 @@ final class TuiSettings {
private String logPin;
private String ratePer;
private String defaultFolder;
+ private String proxyHost;
+ private String proxyPort;
private String aiProvider;
private String aiModel;
private String aiUrl;
@@ -99,6 +106,22 @@ final class TuiSettings {
this.defaultFolder = defaultFolder;
}
+ String getProxyHost() {
+ return proxyHost;
+ }
+
+ void setProxyHost(String proxyHost) {
+ this.proxyHost = proxyHost;
+ }
+
+ String getProxyPort() {
+ return proxyPort;
+ }
+
+ void setProxyPort(String proxyPort) {
+ this.proxyPort = proxyPort;
+ }
+
String getAiProvider() {
return aiProvider;
}
@@ -161,6 +184,8 @@ final class TuiSettings {
settings.logPin = trimToNull(TuiUserConfig.read(PROP_LOG_PIN));
settings.ratePer = trimToNull(TuiUserConfig.read(PROP_RATE_PER));
settings.defaultFolder =
trimToNull(TuiUserConfig.read(PROP_DEFAULT_FOLDER));
+ settings.proxyHost =
trimToNull(TuiUserConfig.read(PROP_PROXY_HOST));
+ settings.proxyPort =
trimToNull(TuiUserConfig.read(PROP_PROXY_PORT));
settings.aiProvider =
trimToNull(TuiUserConfig.read(PROP_AI_PROVIDER));
settings.aiModel = trimToNull(TuiUserConfig.read(PROP_AI_MODEL));
settings.aiUrl = trimToNull(TuiUserConfig.read(PROP_AI_URL));
@@ -185,6 +210,8 @@ final class TuiSettings {
TuiUserConfig.write(PROP_LOG_PIN, logPin);
TuiUserConfig.write(PROP_RATE_PER, ratePer);
TuiUserConfig.write(PROP_DEFAULT_FOLDER, defaultFolder);
+ TuiUserConfig.write(PROP_PROXY_HOST, proxyHost);
+ TuiUserConfig.write(PROP_PROXY_PORT, proxyPort);
TuiUserConfig.write(PROP_AI_PROVIDER, aiProvider);
TuiUserConfig.write(PROP_AI_MODEL, aiModel);
TuiUserConfig.write(PROP_AI_URL, aiUrl);
@@ -195,6 +222,27 @@ final class TuiSettings {
}
}
+ /**
+ * Returns a {@link ProxySelector} based on the configured proxy settings,
falling back to the JVM default.
+ */
+ static ProxySelector proxySelector() {
+ TuiSettings s = load();
+ String host = s.getProxyHost();
+ if (host != null && !host.isEmpty()) {
+ int port = 8080;
+ String portStr = s.getProxyPort();
+ if (portStr != null) {
+ try {
+ port = Integer.parseInt(portStr);
+ } catch (NumberFormatException e) {
+ // use default port
+ }
+ }
+ return ProxySelector.of(new InetSocketAddress(host, port));
+ }
+ return ProxySelector.getDefault();
+ }
+
private static String trimToNull(String value) {
if (value == null) {
return null;
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupTest.java
index cea26dd4af25..f353f54cf057 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupTest.java
@@ -177,7 +177,9 @@ class SettingsPopupTest {
popup.handleKeyEvent(key(KeyCode.DOWN));
popup.handleKeyEvent(key(KeyCode.DOWN));
popup.handleKeyEvent(key(KeyCode.DOWN));
- assertEquals(7, popup.selectedRow());
+ popup.handleKeyEvent(key(KeyCode.DOWN));
+ popup.handleKeyEvent(key(KeyCode.DOWN));
+ assertEquals(9, popup.selectedRow());
assertEquals("auto", popup.selectedAiProvider());
popup.handleKeyEvent(KeyEvent.ofChar(' '));
assertEquals("ollama", popup.selectedAiProvider());
@@ -209,21 +211,21 @@ class SettingsPopupTest {
popup.setTabEntries(tabs());
popup.open();
- // navigate to Shell History (row 6)
- for (int i = 0; i < 6; i++) {
+ // navigate to Shell History (row 8)
+ for (int i = 0; i < 8; i++) {
popup.handleKeyEvent(key(KeyCode.DOWN));
}
- assertEquals(6, popup.selectedRow());
+ assertEquals(8, popup.selectedRow());
for (char c : "50".toCharArray()) {
popup.handleKeyEvent(KeyEvent.ofChar(c));
}
assertEquals("50", popup.shellHistoryText());
- // navigate to AI Prompt History (row 10)
+ // navigate to AI Prompt History (row 12)
for (int i = 0; i < 4; i++) {
popup.handleKeyEvent(key(KeyCode.DOWN));
}
- assertEquals(10, popup.selectedRow());
+ assertEquals(12, popup.selectedRow());
for (char c : "200".toCharArray()) {
popup.handleKeyEvent(KeyEvent.ofChar(c));
}