Skip to content

Commit 3e4a07a

Browse files
committed
- use redirect handling from httpclient
- improve proxy handling (Forward, reverse)
1 parent ec628bf commit 3e4a07a

7 files changed

Lines changed: 203 additions & 81 deletions

File tree

gradle.properties

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
# IntelliJ Platform Artifacts Repositories
22
# -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html
3-
4-
pluginGroup = org.codinjutsu
5-
pluginName = jenkins-control-plugin
3+
pluginGroup=org.codinjutsu
4+
pluginName=jenkins-control-plugin
65
# SemVer format -> https://semver.org
7-
pluginVersion = 0.13.17-eap4
8-
6+
pluginVersion=0.13.17-eap5
97
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
108
# for insight into build numbers and IntelliJ Platform versions.
11-
pluginSincePlatformVersion = 2022.2
12-
pluginSinceBuild = 222.2964
13-
pluginUntilBuild =
14-
9+
pluginSincePlatformVersion=2022.2
10+
pluginSinceBuild=222.2964
11+
pluginUntilBuild=
1512
# IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties
16-
platformType = IC
13+
platformType=IC
1714
#Rider
1815
#platformType = RD
1916
#Android
@@ -25,25 +22,20 @@ platformType = IC
2522
#platformVersion = 222-EAP-SNAPSHOT
2623
#platformVersion = 222-EAP-SNAPSHOT
2724
#platformVersion = 223-EAP-SNAPSHOT
28-
platformVersion = 2022.2
25+
platformVersion=2022.2
2926
#platformVersion = 2022.3
30-
platformVersionToVerify = 222.2964.55
31-
27+
platformVersionToVerify=222.2964.55
3228
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
3329
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22, com.intellij.kubernetes
34-
platformPlugins = com.intellij.java
35-
30+
platformPlugins=com.intellij.java
3631
# Java language level used to compile sources and to generate the files for - Java 11 is required since 2020.3
37-
javaVersion = 11
38-
javaVersionTarget = 11
39-
32+
javaVersion=11
33+
javaVersionTarget=11
4034
# Gradle Releases -> https://github.com/gradle/gradle/releases
41-
gradleVersion = 7.4.2
42-
35+
gradleVersion=7.4.2
4336
# Opt-out flag for bundling Kotlin standard library.
4437
# See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details.
4538
# suppress inspection "UnusedProperty"
46-
kotlin.stdlib.default.dependency = false
47-
39+
kotlin.stdlib.default.dependency=false
4840
# https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#build-features
4941
org.jetbrains.intellij.buildFeature.buildFeatureName=false

src/main/java/org/codinjutsu/tools/jenkins/logic/JenkinsNotifier.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
package org.codinjutsu.tools.jenkins.logic;
22

33
import com.intellij.ide.BrowserUtil;
4+
import com.intellij.ide.DataManager;
45
import com.intellij.notification.*;
6+
import com.intellij.openapi.actionSystem.CommonDataKeys;
57
import com.intellij.openapi.components.Service;
8+
import com.intellij.openapi.diagnostic.Logger;
69
import com.intellij.openapi.project.Project;
10+
import org.codinjutsu.tools.jenkins.util.GuiUtil;
711
import org.jetbrains.annotations.NotNull;
812
import org.jetbrains.annotations.Nullable;
913

14+
import java.util.function.Consumer;
15+
1016
@Service
1117
public final class JenkinsNotifier {
1218

19+
private static final Logger LOG = Logger.getInstance(JenkinsNotifier.class);
20+
1321
private final NotificationGroup jenkinsGroup;
1422
@Nullable
1523
private final Project project;
@@ -25,6 +33,18 @@ public static JenkinsNotifier getInstance(@NotNull Project project) {
2533
return jenkinsNotifier == null ? new JenkinsNotifier(project) : jenkinsNotifier;
2634
}
2735

36+
public static void notifyForCurrentContext(String content, NotificationType notificationType) {
37+
notifyForCurrentContext(jenkinsNotifier -> jenkinsNotifier.notify(content, notificationType));
38+
}
39+
40+
public static void notifyForCurrentContext(Consumer<JenkinsNotifier> jenkinsNotifier) {
41+
GuiUtil.runInSwingThread(() -> DataManager.getInstance().getDataContextFromFocusAsync()
42+
.then(CommonDataKeys.PROJECT::getData)
43+
.then(JenkinsNotifier::getInstance)
44+
.onSuccess(jenkinsNotifier)
45+
.onError(LOG::error));
46+
}
47+
2848
@NotNull
2949
public Notification error(String content) {
3050
return error(project, content);

src/main/java/org/codinjutsu/tools/jenkins/security/BasicSecurityClient.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,14 @@ private String doAuthentication(URL jenkinsUrl) throws AuthenticationException {
5757
final var targetHost = new HttpHost(jenkinsUrl.getHost(), jenkinsUrl.getPort(), jenkinsUrl.getProtocol());
5858
final var credentials = new UsernamePasswordCredentials(username, password);
5959
addAuthenticationPreemptive(targetHost, credentials);
60+
61+
final var redirectTarget = getLastRedirectionHost(targetHost);
62+
addAuthenticationPreemptive(redirectTarget, credentials);
6063
}
6164

6265
final var post = createPost(jenkinsUrl.toString(), Collections.emptyList());
6366
try {
64-
final var response = executeHttpFollowRedirect(post);
67+
final var response = executeHttp(post);
6568
final var responseCode = response.getStatusLine().getStatusCode();
6669
final var responseBody = EntityUtils.toString(response.getEntity());
6770
LOG.trace(String.format("Call url '%s' --> Status: %s, Data %s", jenkinsUrl, responseCode,

src/main/java/org/codinjutsu/tools/jenkins/security/DefaultSecurityClient.java

Lines changed: 51 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -16,65 +16,60 @@
1616

1717
package org.codinjutsu.tools.jenkins.security;
1818

19-
import com.github.cliftonlabs.json_simple.JsonObject;
20-
import com.github.cliftonlabs.json_simple.Jsoner;
19+
import com.intellij.notification.NotificationType;
20+
import com.intellij.openapi.diagnostic.Logger;
2121
import com.intellij.util.net.IdeHttpClientHelpers;
2222
import com.intellij.util.net.ssl.CertificateManager;
2323
import org.apache.commons.lang.StringUtils;
24-
import org.apache.http.HttpHeaders;
2524
import org.apache.http.HttpHost;
2625
import org.apache.http.HttpResponse;
2726
import org.apache.http.auth.AuthScope;
2827
import org.apache.http.auth.UsernamePasswordCredentials;
2928
import org.apache.http.client.AuthCache;
3029
import org.apache.http.client.CredentialsProvider;
3130
import org.apache.http.client.config.RequestConfig;
31+
import org.apache.http.client.methods.HttpHead;
3232
import org.apache.http.client.methods.HttpPost;
33+
import org.apache.http.client.methods.HttpRequestBase;
3334
import org.apache.http.client.methods.HttpUriRequest;
3435
import org.apache.http.client.protocol.HttpClientContext;
3536
import org.apache.http.config.SocketConfig;
36-
import org.apache.http.entity.ContentType;
37-
import org.apache.http.entity.mime.MultipartEntityBuilder;
3837
import org.apache.http.impl.auth.BasicScheme;
3938
import org.apache.http.impl.client.BasicAuthCache;
4039
import org.apache.http.impl.client.BasicCredentialsProvider;
4140
import org.apache.http.impl.client.CloseableHttpClient;
4241
import org.apache.http.impl.client.HttpClients;
43-
import org.apache.http.protocol.HttpContext;
4442
import org.apache.http.util.EntityUtils;
4543
import org.codinjutsu.tools.jenkins.exception.AuthenticationException;
4644
import org.codinjutsu.tools.jenkins.exception.ConfigurationException;
47-
import org.codinjutsu.tools.jenkins.exception.JenkinsPluginRuntimeException;
48-
import org.codinjutsu.tools.jenkins.model.FileParameter;
45+
import org.codinjutsu.tools.jenkins.logic.JenkinsNotifier;
4946
import org.codinjutsu.tools.jenkins.model.RequestData;
50-
import org.codinjutsu.tools.jenkins.model.VirtualFilePart;
5147
import org.jetbrains.annotations.NotNull;
5248
import org.jetbrains.annotations.Nullable;
5349

5450
import javax.net.ssl.SSLContext;
5551
import java.io.IOException;
5652
import java.net.HttpURLConnection;
57-
import java.net.URI;
5853
import java.net.URL;
5954
import java.net.UnknownHostException;
6055
import java.nio.charset.Charset;
6156
import java.nio.charset.StandardCharsets;
6257
import java.util.Collection;
58+
import java.util.Optional;
6359
import java.util.function.Function;
6460

6561
class DefaultSecurityClient implements SecurityClient {
6662

67-
private static final String BAD_CRUMB_DATA = "No valid crumb was included in the request";
6863
static final Charset CHARSET = StandardCharsets.UTF_8;
69-
static final String CHARSET_NAME = CHARSET.name();
64+
private static final Logger LOG = Logger.getInstance(DefaultSecurityClient.class);
65+
private static final String BAD_CRUMB_DATA = "No valid crumb was included in the request";
7066
private final CloseableHttpClient httpClient;
71-
protected @Deprecated String crumbData;
72-
protected JenkinsVersion jenkinsVersion = JenkinsVersion.VERSION_1;
7367
private final CredentialsProvider credentialsProvider;
7468
private final AuthCache authCache;
7569
private final HttpClientContext httpClientContext;
76-
7770
private final Function<String, RequestConfig> configCreator;
71+
protected @Deprecated String crumbData;
72+
protected JenkinsVersion jenkinsVersion = JenkinsVersion.VERSION_1;
7873

7974
DefaultSecurityClient(String crumbData, int connectionTimout) {
8075
this(crumbData, connectionTimout, CertificateManager.getInstance().getSslContext(), true);
@@ -90,13 +85,15 @@ class DefaultSecurityClient implements SecurityClient {
9085
.setSocketTimeout(connectionTimout);
9186
this.credentialsProvider = new BasicCredentialsProvider();
9287

93-
final RequestConfig defaultRequestConfig = requestConfig.build();
88+
final RequestConfig defaultRequestConfig = requestConfig
89+
.setMaxRedirects(10)
90+
.build();
9491
final var httpClientBuilder = HttpClients.custom()
9592
.setSSLContext(sslContext)
9693
.setDefaultSocketConfig(socketConfig)
9794
.setDefaultRequestConfig(defaultRequestConfig)
9895
.setDefaultCredentialsProvider(credentialsProvider)
99-
.disableRedirectHandling();
96+
.setRedirectStrategy(new JenkinsRedirectStrategy());
10097
this.httpClient = httpClientBuilder.build();
10198
if (useProxySettings) {
10299
this.configCreator = url -> {
@@ -125,7 +122,7 @@ public String connect(URL jenkinsUrl) {
125122
public @NotNull Response execute(URL url, @NotNull Collection<RequestData> data) {
126123
String urlStr = url.toString();
127124

128-
ResponseCollector responseCollector = new ResponseCollector();
125+
final var responseCollector = new ResponseCollector();
129126
runMethod(urlStr, data, responseCollector);
130127

131128
if (isRedirection(responseCollector.statusCode)) {
@@ -136,7 +133,7 @@ public String connect(URL jenkinsUrl) {
136133
}
137134

138135
@Override
139-
public @NotNull HttpContext getHttpClientContext() {
136+
public @NotNull HttpClientContext getHttpClientContext() {
140137
return this.httpClientContext;
141138
}
142139

@@ -163,27 +160,12 @@ public String connect(URL jenkinsUrl) {
163160
* </pre>
164161
*/
165162
@NotNull
166-
HttpPost createPost(String url, @NotNull Collection<RequestData> data) {
167-
final var post = new HttpPost(url);
168-
if (!data.isEmpty()) {
169-
final var multipartEntity = MultipartEntityBuilder.create();
170-
171-
data.stream().filter(FileParameter.class::isInstance)
172-
.map(FileParameter.class::cast)
173-
.forEach(fileParameter -> addMultipartBinaryBody(fileParameter, multipartEntity));
174-
175-
final var parameterJson = new JsonObject();
176-
parameterJson.put("parameter", data);
177-
multipartEntity.addTextBody("json", Jsoner.serialize(parameterJson), ContentType.APPLICATION_JSON)
178-
.setCharset(DefaultSecurityClient.CHARSET);
179-
final var multipart = multipartEntity.build();
180-
post.setEntity(multipart);
181-
}
182-
163+
JenkinsPost createPost(String url, @NotNull Collection<RequestData> data) {
164+
final var post = new JenkinsPost(url);
165+
post.setData(data);
183166
if (isCrumbDataSet()) {
184167
post.addHeader(jenkinsVersion.getCrumbName(), crumbData);
185168
}
186-
post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, "en-US,en;q=0.5");
187169
return post;
188170
}
189171

@@ -193,14 +175,34 @@ protected final void addAuthenticationPreemptive(HttpHost host, UsernamePassword
193175
credentialsProvider.setCredentials(authScope, credentials);
194176
}
195177

196-
private void addMultipartBinaryBody(FileParameter fileParameter,
197-
MultipartEntityBuilder multipartEntityBuilder) {
198-
final var virtualFilePart = new VirtualFilePart(fileParameter.getFile());
178+
protected final HttpHost getLastRedirectionHost(HttpHost host) {
179+
final var httpHead = new HttpHead(host.toURI());
199180
try {
200-
multipartEntityBuilder.addBinaryBody(fileParameter.getFileName(), virtualFilePart.createInputStream(),
201-
ContentType.APPLICATION_OCTET_STREAM, virtualFilePart.getFileName());
202-
} catch (IOException e) {
203-
throw new JenkinsPluginRuntimeException(e.getMessage(), e);
181+
final var context = getHttpClientContext();
182+
final var response = executeHttp(httpHead);
183+
final var statusCode = response.getStatusLine().getStatusCode();
184+
185+
var targetHost = host;
186+
if (HttpURLConnection.HTTP_OK == statusCode) {
187+
final var locations = context.getRedirectLocations();
188+
if (locations != null) {
189+
final var lastHost = locations.get(locations.size() - 1).toURL();
190+
targetHost = new HttpHost(lastHost.getHost(), lastHost.getPort(), lastHost.getProtocol());
191+
}
192+
}
193+
return targetHost;
194+
} catch (IOException cause) {
195+
final var errorMessage = Optional.ofNullable(cause.getMessage())
196+
.orElseGet(() -> Optional.ofNullable(cause.getCause())
197+
.map(Throwable::getMessage)
198+
.orElse(String.valueOf(cause.getClass())));
199+
final String message = String.format("IO Error during retrieve last Host for Redirection for URL '%s': %s",
200+
httpHead.getURI(), errorMessage);
201+
LOG.info(message, cause);
202+
JenkinsNotifier.notifyForCurrentContext(message, NotificationType.WARNING);
203+
return host;
204+
} finally {
205+
httpHead.releaseConnection();
204206
}
205207
}
206208

@@ -217,12 +219,12 @@ private void runMethod(String url, @NotNull Collection<RequestData> data, Respon
217219
if (HttpURLConnection.HTTP_OK == statusCode) {
218220
responseCollector.collect(statusCode, responseBody);
219221
} else {
220-
responseCollector.statusCode(statusCode);
222+
responseCollector.collect(statusCode, response.getStatusLine().getReasonPhrase());
221223
}
222224
if (errorHeader != null) {
223225
responseCollector.error(errorHeader.getValue());
224226
}
225-
// ToDo @mcmics enable redirection in httpclient again and try to remove following
227+
// if redirect handling not works or we still get a redirect header we handle it manually
226228
if (isRedirection(statusCode)) {
227229
responseCollector.collect(statusCode, response.getLastHeader("Location").getValue());
228230
}
@@ -241,20 +243,11 @@ protected final HttpResponse executeHttp(HttpPost post) throws IOException {
241243
if (postConfig == null) {
242244
post.setConfig(configCreator.apply(post.getURI().toASCIIString()));
243245
}
244-
245-
return httpClient.execute(post, this.httpClientContext);
246+
return executeHttp((HttpRequestBase) post);
246247
}
247248

248-
protected final HttpResponse executeHttpFollowRedirect(HttpPost post) throws IOException {
249-
var response = executeHttp(post);
250-
final var statusCode = response.getStatusLine().getStatusCode();
251-
252-
if (isRedirection(statusCode)) {
253-
final var location = response.getLastHeader("Location").getValue();
254-
post.setURI(URI.create(location));
255-
response = executeHttp(post);
256-
}
257-
return response;
249+
protected final HttpResponse executeHttp(HttpRequestBase httpRequest) throws IOException {
250+
return getHttpClient().execute(httpRequest, this.httpClientContext);
258251
}
259252

260253
protected void checkResponse(int statusCode, String responseBody) throws AuthenticationException {

0 commit comments

Comments
 (0)