From eda8fe1ae7eee8cf79b765fc08616828875aa466 Mon Sep 17 00:00:00 2001 From: aboyko Date: Sun, 2 Mar 2025 08:17:37 -0500 Subject: [PATCH] `noProxy` setting with wildcard char support Signed-off-by: aboyko --- .../DelegatingStreamConnectionProvider.java | 2 +- .../vscode/boot/app/RestTemplateFactory.java | 56 ++++++++++++++++++- .../boot/app/RestTemplateFactoryTest.java | 51 +++++++++++++++++ 3 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/app/RestTemplateFactoryTest.java diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java index 2899c82eba..3c38ac2ab7 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java @@ -277,7 +277,7 @@ private static void fillProxyData(Map proxy, IProxyData data, St proxy.put("proxy-user", data.getUserId()); proxy.put("proxy-password", data.getPassword()); } - proxy.put("proxy-exclusions", exclusions); + proxy.put("noProxy", exclusions); } private void putValidationPreferences(Map settings) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RestTemplateFactory.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RestTemplateFactory.java index 2fa93f9b78..8329e3d592 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RestTemplateFactory.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RestTemplateFactory.java @@ -22,9 +22,14 @@ import java.net.URL; import java.net.http.HttpClient; import java.net.http.HttpClient.Builder; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,11 +43,59 @@ public class RestTemplateFactory { private static final Logger log = LoggerFactory.getLogger(RestTemplateFactory.class); private BootJavaConfig config; + + private HostExclusions proxyExclusions; public RestTemplateFactory(BootJavaConfig config) { this.config = config; + this.proxyExclusions = null; + config.addListener(v -> { + synchronized(RestTemplateFactory.this) { + proxyExclusions = null; + } + }); } + record HostExclusions(Set hosts, List regexs) { + + public HostExclusions(Collection exclusions) { + this(new HashSet<>(), new ArrayList<>()); + for (String s : exclusions) { + if (s.contains("*")) { + // Regex + String regexStr = s.replace(".", "\\.").replace("*", ".*"); + try { + regexs.add(Pattern.compile(regexStr)); + } catch (PatternSyntaxException e) { + log.error("Unnable to compile Regular Expression for %s".formatted(s), e); + } + } else { + // Exact host string + hosts.add(s); + } + } + } + + boolean contains(String host) { + if (hosts.contains(host)) { + return true; + } + for (Pattern p : regexs) { + if (p.matcher(host).matches()) { + return true; + } + } + return false; + } + } + + private synchronized HostExclusions getProxyExclusions() { + if (proxyExclusions == null) { + proxyExclusions = new HostExclusions(config.getRawSettings().getStringSet("http", "noProxy")); + } + return proxyExclusions; + } + public RestTemplate createRestTemplate(String host) { String proxyUrlStr = config.getRawSettings().getString("http", "proxy"); if (proxyUrlStr == null || proxyUrlStr.isBlank()) { @@ -51,8 +104,7 @@ public RestTemplate createRestTemplate(String host) { Builder clientBuilder = HttpClient.newBuilder(); if (proxyUrlStr != null && !proxyUrlStr.isBlank()) { - Set exclusions = config.getRawSettings().getStringSet("http", "proxy-exclusions"); - if (!"localhost".equals(host) && !"127.0.0.1".equals(host) && !exclusions.contains(host)) { + if (!"localhost".equals(host) && !"127.0.0.1".equals(host) && !getProxyExclusions().contains(host)) { try { URL proxyUrl = new URL(proxyUrlStr); clientBuilder.proxy(new ProxySelector() { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/app/RestTemplateFactoryTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/app/RestTemplateFactoryTest.java new file mode 100644 index 0000000000..fb5791bfbc --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/app/RestTemplateFactoryTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2025 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.app; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; +import org.springframework.ide.vscode.boot.app.RestTemplateFactory.HostExclusions; + +public class RestTemplateFactoryTest { + + @Test + void testHostExclusionSet() { + HostExclusions exclusions = new RestTemplateFactory.HostExclusions(List.of("my-host", "foo-*", "*.bar")); + assertEquals(exclusions.hosts(), Set.of("my-host")); + assertEquals(exclusions.regexs().stream().map(r -> r.pattern()).collect(Collectors.toList()), List.of("foo-.*", ".*\\.bar")); + + assertTrue(exclusions.contains("my-host")); + assertFalse(exclusions.contains("my_host")); + + assertTrue(exclusions.contains("foo-bar")); + assertTrue(exclusions.contains("foo-bar-baz")); + assertFalse(exclusions.contains("bar-foo")); + + assertTrue(exclusions.contains("foo.bar")); + assertTrue(exclusions.contains(".bar")); + assertFalse(exclusions.contains("foo.bar.baz")); + assertFalse(exclusions.contains("bar.foo")); + + exclusions = new RestTemplateFactory.HostExclusions(List.of("*")); + assertTrue(exclusions.contains("my_host")); + assertTrue(exclusions.contains("foo.bar.baz")); + assertTrue(exclusions.contains("bar.foo")); + + } + +}