Skip to content

Commit

Permalink
Http Proxy server support
Browse files Browse the repository at this point in the history
  • Loading branch information
BoykoAlex committed Sep 13, 2023
1 parent 5dba01e commit d7ae292
Show file tree
Hide file tree
Showing 15 changed files with 203 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0",
org.eclipse.core.expressions,
org.springsource.ide.eclipse.commons.core;bundle-version="4.17.0",
org.springframework.tooling.jdt.ls.commons;bundle-version="4.17.0",
org.eclipse.jface.notifications
org.eclipse.jface.notifications,
org.eclipse.core.net
Import-Package: com.google.common.base,
com.google.common.collect,
com.google.gson,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
import java.util.List;
import java.util.Map;

import org.eclipse.core.internal.net.ProxyManager;
import org.eclipse.core.net.proxy.IProxyChangeListener;
import org.eclipse.core.net.proxy.IProxyData;
import org.eclipse.core.net.proxy.IProxyService;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
Expand All @@ -32,6 +36,7 @@
import org.eclipse.lsp4j.jsonrpc.messages.Message;
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage;
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.ui.PlatformUI;
import org.springframework.tooling.boot.ls.prefs.CategoryProblemsSeverityPrefsPage;
import org.springframework.tooling.boot.ls.prefs.FileListEditor;
import org.springframework.tooling.boot.ls.prefs.ProblemCategoryData;
Expand All @@ -55,6 +60,7 @@
*
* @author Martin Lippert
*/
@SuppressWarnings("restriction")
public class DelegatingStreamConnectionProvider implements StreamConnectionProvider {

private StreamConnectionProvider provider;
Expand All @@ -70,8 +76,7 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi

private final ValueListener<ImmutableSet<RemoteAppData>> remoteAppsListener = (e, v) -> sendConfiguration();

private long timestampBeforeStart;
private long timestampWhenInitialized;
private final IProxyChangeListener proxySettingsListener = e -> sendConfiguration();

public DelegatingStreamConnectionProvider() {
// LanguageServerCommonsActivator.logInfo("Entering DelegatingStreamConnectionProvider()");
Expand All @@ -94,9 +99,13 @@ public Object getInitializationOptions(URI rootUri) {

@Override
public void start() throws IOException {
this.timestampBeforeStart = System.currentTimeMillis();
BootLanguageServerPlugin.getDefault().getLog().info("DelegatingStreamConnectionProvider - Starting Boot LS");
this.provider.start();
IProxyService proxyService = PlatformUI.getWorkbench().getService(IProxyService.class);
if (proxyService != null) {
proxyService.addProxyChangeListener(proxySettingsListener);
}

}

@Override
Expand All @@ -116,6 +125,10 @@ public InputStream getErrorStream() {

@Override
public void stop() {
IProxyService proxyService = PlatformUI.getWorkbench().getService(IProxyService.class);
if (proxyService != null) {
proxyService.removeProxyChangeListener(proxySettingsListener);
}
BootLanguageServerPlugin.getDefault().getLog().info("DelegatingStreamConnectionProvider - Stopping Boot LS");
this.provider.stop();
if (fResourceListener != null) {
Expand All @@ -134,8 +147,6 @@ public void handleMessage(Message message, LanguageServer languageServer, URI ro
if (responseMessage.getResult() instanceof InitializeResult) {
this.languageServer = languageServer;

this.timestampWhenInitialized = System.currentTimeMillis();
// LanguageServerCommonsActivator.logInfo("Boot LS startup time from start to initialized: " + (timestampWhenInitialized - timestampBeforeStart) + "ms");

sendConfiguration();

Expand Down Expand Up @@ -214,12 +225,47 @@ private void sendConfiguration() {

settings.put("boot-java", bootJavaObj);

settings.put("http", createHttpProxySettings());

putValidationPreferences(settings);
putValidationCategoryToggles(settings);

this.languageServer.getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(settings));
}

private Map<String, Object> createHttpProxySettings() {
Map<String, Object> proxy = new HashMap<>();
IProxyService proxyService = PlatformUI.getWorkbench().getService(IProxyService.class);
if ((proxyService.isProxiesEnabled() || proxyService.isSystemProxiesEnabled()) && proxyService instanceof ProxyManager) {
ProxyManager proxyManager = (ProxyManager) proxyService;
if (proxyService.isSystemProxiesEnabled() && proxyManager.hasSystemProxies()) {
for (IProxyData data : proxyManager.getNativeProxyData()) {
if (data.getHost() != null) {
fillProxyData(proxy, data, proxyManager.getNativeNonProxiedHosts());
break;
}
}
} else if (proxyService.isProxiesEnabled()) {
for (IProxyData data : proxyService.getProxyData()) {
if (data.getHost() != null) {
fillProxyData(proxy, data, proxyService.getNonProxiedHosts());
break;
}
}
}
}
return proxy;
}

private static void fillProxyData(Map<String, Object> proxy, IProxyData data, String[] exclusions) {
proxy.put("proxy", data.getType().toLowerCase() + "://" + data.getHost() + (data.getPort() >= 0 ? ":" + data.getPort() : ""));
if (data.isRequiresAuthentication()) {
proxy.put("proxy-user", data.getUserId());
proxy.put("proxy-password", data.getPassword());
}
proxy.put("proxy-exclusions", exclusions);
}

private void putValidationPreferences(Map<String, Object> settings) {
try {
IEclipsePreferences prefs = BootLanguageServerPlugin.getPreferences();
Expand Down Expand Up @@ -253,6 +299,7 @@ private void putValidationCategoryToggles(Map<String, Object> settings) {
}
}

@SuppressWarnings("unchecked")
private void dotPut(Object _settings, String dottedProperty, Object value) {
if (_settings instanceof Map) {
Map<String, Object> settings = (Map<String, Object>) _settings;
Expand Down
4 changes: 4 additions & 0 deletions headless-services/spring-boot-language-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@
</exclusions>
</dependency>

<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>

<!-- Test harness -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import org.springframework.ide.vscode.boot.common.ProjectReconcileScheduler;
import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade;
import org.springframework.ide.vscode.boot.validation.BootVersionValidationEngine;
import org.springframework.ide.vscode.boot.validation.generations.BootVersionsFromMavenCentral;
import org.springframework.ide.vscode.boot.validation.generations.CachedBootVersionsFromMavenCentral;
import org.springframework.ide.vscode.boot.validation.generations.GenerationsValidator;
import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider;
import org.springframework.ide.vscode.boot.validation.generations.UpdateBootVersion;
Expand All @@ -36,18 +38,26 @@ public class BootVersionValidationConfig {

private static final Logger log = LoggerFactory.getLogger(BootVersionValidationConfig.class);

@Bean UpdateBootVersion updateBootVersion(SimpleLanguageServer server, Optional<SpringBootUpgrade> bootUpgradeOpt) {
return new UpdateBootVersion(server.getDiagnosticSeverityProvider(), bootUpgradeOpt);
@Bean UpdateBootVersion updateBootVersion(SimpleLanguageServer server, Optional<SpringBootUpgrade> bootUpgradeOpt, CachedBootVersionsFromMavenCentral cachedVersionsFromMaven) {
return new UpdateBootVersion(server.getDiagnosticSeverityProvider(), bootUpgradeOpt, cachedVersionsFromMaven);
}

@Bean GenerationsValidator generationsValidator(SimpleLanguageServer server, BootJavaConfig config) {
return new GenerationsValidator(server.getDiagnosticSeverityProvider(), config);
@Bean GenerationsValidator generationsValidator(SimpleLanguageServer server, BootJavaConfig config, RestTemplateFactory restTemplateFactory, CachedBootVersionsFromMavenCentral cachedVersionsFromMaven) {
return new GenerationsValidator(server.getDiagnosticSeverityProvider(), config, restTemplateFactory, cachedVersionsFromMaven);
}

@Bean ProjectVersionDiagnosticProvider projectVersionDiagnosticProvider(List<VersionValidator> validators) {
return new ProjectVersionDiagnosticProvider(validators);
}

@Bean BootVersionsFromMavenCentral booVersionsFromMavenCentral(RestTemplateFactory restTemplateFactory) {
return new BootVersionsFromMavenCentral(restTemplateFactory);
}

@Bean CachedBootVersionsFromMavenCentral cachedBootVersionsFromMavenCentral(BootVersionsFromMavenCentral bootVersionsFromMaven) {
return new CachedBootVersionsFromMavenCentral(bootVersionsFromMaven);
}

@ConditionalOnMissingClass("org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness")
@ConditionalOnProperty(prefix = "languageserver", name = "reconcile-only-opened-docs", havingValue = "false", matchIfMissing = true)
@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*******************************************************************************
* Copyright (c) 2023 VMware, 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:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.app;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;

@Component
public class RestTemplateFactory {

private static final Logger log = LoggerFactory.getLogger(RestTemplateFactory.class);

private BootJavaConfig config;

public RestTemplateFactory(BootJavaConfig config) {
this.config = config;
}

public RestTemplate createRestTemplate(String host) {
String proxyUrl = config.getRawSettings().getString("http", "proxy");
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
if (proxyUrl != null && !proxyUrl.isBlank()) {
Set<String> exclusions = config.getRawSettings().getStringSet("http", "proxy-exclusions");
if (!"localhost".equals(host) && !"127.0.0.1".equals(host) && !exclusions.contains(host)) {
try {
URL url = new URL(proxyUrl);
if (url.getProtocol().startsWith("http")) {
clientBuilder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(url.getHost(), url.getPort())));
} else if (url.getProtocol().startsWith("sock")) {
clientBuilder.proxy(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(url.getHost(), url.getPort())));
}
String username = config.getRawSettings().getString("http", "proxy-user");
String password = config.getRawSettings().getString("http", "proxy-password");
if (username != null && password != null && !username.isEmpty()) {
clientBuilder.proxyAuthenticator(new Authenticator() {
@Override
public Request authenticate(Route route, Response response) throws IOException {
String credential = Credentials.basic(username, password);
return response.request().newBuilder().header("Proxy-Authorization", credential)
.build();
}
});
}
} catch (MalformedURLException e) {
log.error("", e);
}
}
}
return new RestTemplate(new OkHttp3ClientHttpRequestFactory(clientBuilder.build()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package org.springframework.ide.vscode.boot.validation.generations;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand All @@ -24,22 +25,28 @@
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.ide.vscode.boot.app.RestTemplateFactory;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
import org.springframework.web.client.RestTemplate;

public class BootVersionsFromMavenCentral {

private static final Logger log = LoggerFactory.getLogger(BootVersionsFromMavenCentral.class);
private static final String URL = "https://search.maven.org/solrsearch/select?q=g:org.springframework.boot+AND+a:spring-boot-starter-parent&core=gav&rows=200&wt=json";
private static final URI URL = URI.create("https://search.maven.org/solrsearch/select?q=g:org.springframework.boot+AND+a:spring-boot-starter-parent&core=gav&rows=200&wt=json");
private RestTemplateFactory restTemplateFactory;

public BootVersionsFromMavenCentral(RestTemplateFactory restTemplateFactory) {
this.restTemplateFactory = restTemplateFactory;
}

@SuppressWarnings({ "rawtypes", "unchecked" })
public static List<Version> getBootVersions() throws IOException {
public List<Version> getBootVersions() throws IOException {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(MediaType.parseMediaTypes("application/json"));

HttpEntity<?> entity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
RestTemplate restTemplate = restTemplateFactory.createRestTemplate(URL.getHost());

log.info("search maven central for Spring Boot release information via: " + URL);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@ public class CachedBootVersionsFromMavenCentral {
private static final Duration EXPIRES_AFTER = Duration.ofMinutes(60);
private static final int ATTEMPTS_NUMBER = 5;
private static final long RESPONSE_WAIT_TIME_MS = 1000;

private BootVersionsFromMavenCentral bootVersionsFromMaven;

private static final LoadingCache<String, List<Version>> cache = CacheBuilder.newBuilder()
public CachedBootVersionsFromMavenCentral(BootVersionsFromMavenCentral bootVersionsFromMaven) {
this.bootVersionsFromMaven = bootVersionsFromMaven;
}

private final LoadingCache<String, List<Version>> cache = CacheBuilder.newBuilder()
.expireAfterWrite(EXPIRES_AFTER)
.build(new CacheLoader<String, List<Version>>() {

Expand All @@ -62,7 +68,7 @@ public List<Version> load(String key) throws Exception {

});

public static synchronized List<Version> getBootVersions() {
public synchronized List<Version> getBootVersions() {
try {
return cache.get(KEY);
}
Expand All @@ -72,10 +78,10 @@ public static synchronized List<Version> getBootVersions() {
}
}

private static CompletableFuture<List<Version>> getFuture() {
private CompletableFuture<List<Version>> getFuture() {
return CompletableFuture.supplyAsync(() -> {
try {
return BootVersionsFromMavenCentral.getBootVersions();
return bootVersionsFromMaven.getBootVersions();
} catch (IOException e) {
throw new CompletionException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import org.eclipse.lsp4j.Diagnostic;
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
import org.springframework.ide.vscode.boot.app.RestTemplateFactory;
import org.springframework.ide.vscode.boot.validation.generations.json.Generation;
import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject;
import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationProblemType;
Expand All @@ -30,9 +31,9 @@ public class GenerationsValidator extends AbstractDiagnosticValidator {

private SpringIoProjectsProvider provider;

public GenerationsValidator(DiagnosticSeverityProvider diagnosticSeverityProvider, BootJavaConfig config) {
public GenerationsValidator(DiagnosticSeverityProvider diagnosticSeverityProvider, BootJavaConfig config, RestTemplateFactory restTemplateFactory, CachedBootVersionsFromMavenCentral cachedVersionsFromMaven) {
super(diagnosticSeverityProvider);
provider = new SpringIoProjectsProvider(config.getSpringIOApiUrl());
provider = new SpringIoProjectsProvider(config.getSpringIOApiUrl(), restTemplateFactory, cachedVersionsFromMaven);
config.addListener(v -> provider.updateIoApiUri(config.getSpringIOApiUrl()));
}

Expand Down
Loading

0 comments on commit d7ae292

Please sign in to comment.