19
19
import java .util .HashMap ;
20
20
import java .util .Map ;
21
21
import java .util .concurrent .CountDownLatch ;
22
- import java .util .concurrent .ExecutionException ;
23
22
import java .util .concurrent .TimeUnit ;
24
- import java .util .concurrent .TimeoutException ;
25
23
26
24
import org .eclipse .jdt .annotation .NonNullByDefault ;
27
25
import org .eclipse .jdt .annotation .Nullable ;
28
26
import org .eclipse .jetty .client .HttpClient ;
29
- import org .eclipse .jetty .client .api .ContentResponse ;
30
27
import org .eclipse .jetty .client .api .Request ;
31
28
import org .eclipse .jetty .client .api .Response ;
32
29
import org .eclipse .jetty .http .HttpHeader ;
@@ -69,18 +66,18 @@ private static Map<String, String> getAuthParams(HttpClient httpClient, URI logi
69
66
throws IOException {
70
67
LOGGER .debug ("Sending login request to get authentication challenge" );
71
68
CountDownLatch latch = new CountDownLatch (1 );
72
- Request initialRequest = httpClient .newRequest (loginUri ).timeout (timeout , TimeUnit .MILLISECONDS );
73
- XWwwAuthenticateHeaderListener XWwwAuthenticateHeaderListener = new XWwwAuthenticateHeaderListener (latch );
74
- initialRequest .onResponseHeaders (XWwwAuthenticateHeaderListener );
75
- initialRequest .send (result -> latch .countDown ());
69
+ Request request = httpClient .newRequest (loginUri ).timeout (timeout , TimeUnit .MILLISECONDS );
70
+ XWwwAuthenticateHeaderListener xWwwAuthenticateHeaderListener = new XWwwAuthenticateHeaderListener (latch );
71
+ request .onResponseHeaders (xWwwAuthenticateHeaderListener );
72
+ request .send (result -> latch .countDown ());
76
73
// Wait for the request to complete
77
74
try {
78
75
latch .await ();
79
76
} catch (InterruptedException ie ) {
80
77
throw new RuntimeException (ie );
81
78
}
82
79
83
- String authHeader = XWwwAuthenticateHeaderListener .getAuthHeader ();
80
+ String authHeader = xWwwAuthenticateHeaderListener .getAuthHeader ();
84
81
if (authHeader == null ) {
85
82
throw new IOException ("No authentication header found in login response" );
86
83
}
@@ -161,21 +158,40 @@ private static String md5Hex(String data) {
161
158
* @param authHeader the authentication header to use for the login request
162
159
* @throws InterruptedException when the request is interrupted
163
160
* @throws FroniusCommunicationException when the login request failed
161
+ * @throws FroniusUnauthorizedException when the login failed due to invalid credentials
164
162
*/
165
163
private static void performLoginRequest (HttpClient httpClient , URI loginUri , String authHeader , int timeout )
166
- throws InterruptedException , FroniusCommunicationException {
167
- Request loginRequest = httpClient .newRequest (loginUri ).header (HttpHeader .AUTHORIZATION , authHeader )
168
- .timeout (timeout , TimeUnit .MILLISECONDS );
169
- ContentResponse loginResponse ;
164
+ throws InterruptedException , FroniusCommunicationException , FroniusUnauthorizedException {
165
+ CountDownLatch latch = new CountDownLatch (1 );
166
+ Request request = httpClient .newRequest (loginUri ).header (HttpHeader .AUTHORIZATION , authHeader ).timeout (timeout ,
167
+ TimeUnit .MILLISECONDS );
168
+ StatusListener statusListener = new StatusListener (latch );
169
+ request .onResponseBegin (statusListener );
170
+ Integer status ;
170
171
try {
171
- loginResponse = loginRequest .send ();
172
- if (loginResponse .getStatus () != 200 ) {
173
- throw new FroniusCommunicationException (
174
- "Failed to send login request, status code: " + loginResponse .getStatus ());
172
+ request .send (result -> latch .countDown ());
173
+ // Wait for the request to complete
174
+ try {
175
+ latch .await ();
176
+ } catch (InterruptedException ie ) {
177
+ throw new RuntimeException (ie );
178
+ }
179
+
180
+ status = statusListener .getStatus ();
181
+ if (status == null ) {
182
+ throw new FroniusCommunicationException ("Failed to send login request: No status code received." );
175
183
}
176
- } catch (TimeoutException | ExecutionException e ) {
184
+ } catch (IOException e ) {
177
185
throw new FroniusCommunicationException ("Failed to send login request" , e );
178
186
}
187
+
188
+ if (status == 401 ) {
189
+ throw new FroniusUnauthorizedException (
190
+ "Failed to send login request, status code: 401 Unauthorized. Please check your credentials." );
191
+ }
192
+ if (status != 200 ) {
193
+ throw new FroniusCommunicationException ("Failed to send login request, status code: " + status );
194
+ }
179
195
}
180
196
181
197
/**
@@ -191,9 +207,11 @@ private static void performLoginRequest(HttpClient httpClient, URI loginUri, Str
191
207
* @param timeout the timeout in milliseconds for the login requests
192
208
* @return the authentication header for the next request
193
209
* @throws FroniusCommunicationException when the login failed or interrupted
210
+ * @throws FroniusUnauthorizedException when the login failed due to invalid credentials
194
211
*/
195
212
public static synchronized String login (HttpClient httpClient , URI baseUri , String username , String password ,
196
- HttpMethod method , String relativeUrl , int timeout ) throws FroniusCommunicationException {
213
+ HttpMethod method , String relativeUrl , int timeout )
214
+ throws FroniusCommunicationException , FroniusUnauthorizedException {
197
215
// Perform request to get authentication parameters
198
216
LOGGER .debug ("Getting authentication parameters" );
199
217
URI loginUri = baseUri .resolve (URI .create (LOGIN_ENDPOINT + "?user=" + username ));
@@ -246,6 +264,8 @@ public static synchronized String login(HttpClient httpClient, URI baseUri, Stri
246
264
Thread .sleep (500 * attemptCount );
247
265
attemptCount ++;
248
266
lastException = e ;
267
+ } catch (FroniusUnauthorizedException e ) {
268
+ throw e ;
249
269
}
250
270
251
271
if (attemptCount >= 3 ) {
@@ -269,6 +289,9 @@ public static synchronized String login(HttpClient httpClient, URI baseUri, Stri
269
289
270
290
/**
271
291
* Listener to extract the X-Www-Authenticate header from the response of a {@link Request}.
292
+ * Required to mitigate {@link org.eclipse.jetty.client.HttpResponseException}: HTTP protocol violation:
293
+ * Authentication challenge without WWW-Authenticate header being thrown due to Fronius non-standard authentication
294
+ * header.
272
295
*/
273
296
private static class XWwwAuthenticateHeaderListener extends Response .Listener .Adapter {
274
297
private final CountDownLatch latch ;
@@ -288,4 +311,30 @@ public void onHeaders(Response response) {
288
311
return authHeader ;
289
312
}
290
313
}
314
+
315
+ /**
316
+ * Listener to extract the HTTP status code from the response of a {@link Request} on response begin.
317
+ * Required to mitigate {@link org.eclipse.jetty.client.HttpResponseException}: HTTP protocol violation:
318
+ * Authentication challenge without WWW-Authenticate header being thrown due to Fronius non-standard authentication
319
+ * header.
320
+ */
321
+ private static class StatusListener extends Response .Listener .Adapter {
322
+ private final CountDownLatch latch ;
323
+ private @ Nullable Integer status ;
324
+
325
+ public StatusListener (CountDownLatch latch ) {
326
+ this .latch = latch ;
327
+ }
328
+
329
+ @ Override
330
+ public void onBegin (Response response ) {
331
+ this .status = response .getStatus ();
332
+ latch .countDown ();
333
+ super .onBegin (response );
334
+ }
335
+
336
+ public @ Nullable Integer getStatus () {
337
+ return status ;
338
+ }
339
+ }
291
340
}
0 commit comments