49
49
import java .util .List ;
50
50
import java .util .Map ;
51
51
52
+ import javax .annotation .Priority ;
52
53
import javax .ws .rs .Priorities ;
53
54
import javax .ws .rs .client .Client ;
54
- import javax .ws .rs .client .ClientBuilder ;
55
55
import javax .ws .rs .client .ClientRequestContext ;
56
56
import javax .ws .rs .client .ClientRequestFilter ;
57
57
import javax .ws .rs .client .ClientResponseContext ;
66
66
import javax .ws .rs .core .MultivaluedMap ;
67
67
import javax .ws .rs .core .Response ;
68
68
69
- import javax .annotation .Priority ;
70
-
71
69
import org .glassfish .jersey .client .ClientProperties ;
72
70
import org .glassfish .jersey .client .internal .LocalizationMessages ;
73
71
@@ -222,15 +220,21 @@ public void filter(ClientRequestContext request, ClientResponseContext response)
222
220
Type result = null ; // which authentication is requested: BASIC or DIGEST
223
221
boolean authenticate ;
224
222
223
+ // If the server requests both BASIC and DIGEST, prefer DIGEST since it's stronger
224
+ // (see https://tools.ietf.org/html/rfc2617#section-4.6)
225
225
if (response .getStatus () == Response .Status .UNAUTHORIZED .getStatusCode ()) {
226
- String authString = response .getHeaders ().getFirst (HttpHeaders .WWW_AUTHENTICATE );
227
- if (authString != null ) {
228
- final String upperCaseAuth = authString .trim ().toUpperCase ();
229
- if (upperCaseAuth .startsWith ("BASIC" )) {
230
- result = Type .BASIC ;
231
- } else if (upperCaseAuth .startsWith ("DIGEST" )) {
232
- result = Type .DIGEST ;
233
- } else {
226
+ List <String > authStrings = response .getHeaders ().get (HttpHeaders .WWW_AUTHENTICATE );
227
+ if (authStrings != null ) {
228
+ for (String authString : authStrings ) {
229
+ final String upperCaseAuth = authString .trim ().toUpperCase ();
230
+ if (result == null && upperCaseAuth .startsWith ("BASIC" )) {
231
+ result = Type .BASIC ;
232
+ } else if (upperCaseAuth .startsWith ("DIGEST" )) {
233
+ result = Type .DIGEST ;
234
+ }
235
+ }
236
+
237
+ if (result == null ) {
234
238
// unknown authentication -> this filter cannot authenticate with this method
235
239
return ;
236
240
}
@@ -292,10 +296,20 @@ private void updateCache(ClientRequestContext request, boolean success, Type ope
292
296
* @param newAuthorizationHeader {@code Authorization} header that should be added to the new request.
293
297
* @return {@code true} is the authentication was successful ({@code true} if 401 response code was not returned;
294
298
* {@code false} otherwise).
299
+ * @throws IOException
295
300
*/
296
- static boolean repeatRequest (ClientRequestContext request , ClientResponseContext response , String newAuthorizationHeader ) {
301
+ static boolean repeatRequest (ClientRequestContext request , ClientResponseContext response , String newAuthorizationHeader )
302
+ throws IOException {
303
+ // If the failed response has an entity stream, close it. We must do this to avoid leaking a connection
304
+ // when we replace the entity stream of the failed response with that of the repeated response (see below).
305
+ // Notice that by closing the entity stream before sending the repeated request we allow the connection allocated
306
+ // to the failed request to be reused, if possible, for the repeated request.
307
+ if (response .hasEntity ()) {
308
+ response .getEntityStream ().close ();
309
+ response .setEntityStream (null );
310
+ }
311
+
297
312
Client client = request .getClient ();
298
-
299
313
String method = request .getMethod ();
300
314
MediaType mediaType = request .getMediaType ();
301
315
URI lUri = request .getUri ();
@@ -318,6 +332,12 @@ static boolean repeatRequest(ClientRequestContext request, ClientResponseContext
318
332
319
333
builder .property (REQUEST_PROPERTY_FILTER_REUSED , "true" );
320
334
335
+ // Copy other properties, if any, from the original request
336
+ for (String propertyName : request .getPropertyNames ()) {
337
+ Object propertyValue = request .getProperty (propertyName );
338
+ builder .property (propertyName , propertyValue );
339
+ }
340
+
321
341
Invocation invocation ;
322
342
if (request .getEntity () == null ) {
323
343
invocation = builder .build (method );
0 commit comments