1616
1717package 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 ;
2121import com .intellij .util .net .IdeHttpClientHelpers ;
2222import com .intellij .util .net .ssl .CertificateManager ;
2323import org .apache .commons .lang .StringUtils ;
24- import org .apache .http .HttpHeaders ;
2524import org .apache .http .HttpHost ;
2625import org .apache .http .HttpResponse ;
2726import org .apache .http .auth .AuthScope ;
2827import org .apache .http .auth .UsernamePasswordCredentials ;
2928import org .apache .http .client .AuthCache ;
3029import org .apache .http .client .CredentialsProvider ;
3130import org .apache .http .client .config .RequestConfig ;
31+ import org .apache .http .client .methods .HttpHead ;
3232import org .apache .http .client .methods .HttpPost ;
33+ import org .apache .http .client .methods .HttpRequestBase ;
3334import org .apache .http .client .methods .HttpUriRequest ;
3435import org .apache .http .client .protocol .HttpClientContext ;
3536import org .apache .http .config .SocketConfig ;
36- import org .apache .http .entity .ContentType ;
37- import org .apache .http .entity .mime .MultipartEntityBuilder ;
3837import org .apache .http .impl .auth .BasicScheme ;
3938import org .apache .http .impl .client .BasicAuthCache ;
4039import org .apache .http .impl .client .BasicCredentialsProvider ;
4140import org .apache .http .impl .client .CloseableHttpClient ;
4241import org .apache .http .impl .client .HttpClients ;
43- import org .apache .http .protocol .HttpContext ;
4442import org .apache .http .util .EntityUtils ;
4543import org .codinjutsu .tools .jenkins .exception .AuthenticationException ;
4644import 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 ;
4946import org .codinjutsu .tools .jenkins .model .RequestData ;
50- import org .codinjutsu .tools .jenkins .model .VirtualFilePart ;
5147import org .jetbrains .annotations .NotNull ;
5248import org .jetbrains .annotations .Nullable ;
5349
5450import javax .net .ssl .SSLContext ;
5551import java .io .IOException ;
5652import java .net .HttpURLConnection ;
57- import java .net .URI ;
5853import java .net .URL ;
5954import java .net .UnknownHostException ;
6055import java .nio .charset .Charset ;
6156import java .nio .charset .StandardCharsets ;
6257import java .util .Collection ;
58+ import java .util .Optional ;
6359import java .util .function .Function ;
6460
6561class 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