19
19
import com .ning .http .client .FluentCaseInsensitiveStringsMap ;
20
20
import com .ning .http .client .MaxRedirectException ;
21
21
import com .ning .http .client .Realm ;
22
+ import com .ning .http .client .Realm .AuthScheme ;
22
23
import com .ning .http .client .Request ;
23
24
import com .ning .http .client .RequestBuilder ;
24
25
import com .ning .http .client .cookie .CookieDecoder ;
55
56
import org .slf4j .LoggerFactory ;
56
57
57
58
import static com .ning .http .client .providers .netty .util .HttpUtils .getNTLM ;
59
+ import static com .ning .http .util .AsyncHttpProviderUtils .*;
58
60
import static com .ning .http .util .MiscUtils .isNonEmpty ;
59
61
/**
60
62
* AHC {@link HttpClientFilter} implementation.
@@ -152,7 +154,8 @@ protected void onHttpContentEncoded(final HttpContent content,
152
154
final AsyncHandler handler = context .getAsyncHandler ();
153
155
if (handler instanceof TransferCompletionHandler ) {
154
156
final int written = content .getContent ().remaining ();
155
- final long total = context .totalBodyWritten .addAndGet (written );
157
+ context .totalBodyWritten += written ;
158
+ final long total = context .totalBodyWritten ;
156
159
((TransferCompletionHandler ) handler ).onContentWriteProgress (
157
160
written , total , content .getHttpHeader ().getContentLength ());
158
161
}
@@ -203,15 +206,13 @@ protected void onInitialLineParsed(final HttpHeader httpHeader,
203
206
if (context .statusHandler == null ) {
204
207
context .statusHandler = RedirectHandler .INSTANCE ;
205
208
}
206
- context .redirectCount . incrementAndGet () ;
209
+ context .redirectCount ++ ;
207
210
if (redirectCountExceeded (context )) {
208
211
httpHeader .setSkipRemainder (true );
209
212
context .abort (new MaxRedirectException ());
210
213
}
211
214
} else {
212
- if (context .redirectCount .get () > 0 ) {
213
- context .redirectCount .set (0 );
214
- }
215
+ context .redirectCount = 0 ;
215
216
}
216
217
}
217
218
final GrizzlyResponseStatus responseStatus =
@@ -460,7 +461,7 @@ private static void cleanup(final HttpContext httpContext) {
460
461
}
461
462
462
463
private static boolean redirectCountExceeded (final HttpTransactionContext context ) {
463
- return context .redirectCount . get () > context .maxRedirectCount ;
464
+ return context .redirectCount > context .maxRedirectCount ;
464
465
}
465
466
466
467
private static boolean isRedirect (final int status ) {
@@ -497,10 +498,8 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
497
498
final GrizzlyAsyncHttpProvider provider =
498
499
httpTransactionContext .provider ;
499
500
500
- Realm realm = httpTransactionContext .getAhcRequest ().getRealm ();
501
- if (realm == null ) {
502
- realm = provider .getClientConfig ().getRealm ();
503
- }
501
+ Realm realm = getRealm (httpTransactionContext );
502
+
504
503
if (realm == null ) {
505
504
httpTransactionContext .invocationStatus = InvocationStatus .STOP ;
506
505
final AsyncHandler ah = httpTransactionContext .getAsyncHandler ();
@@ -515,6 +514,7 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
515
514
}
516
515
return true ;
517
516
}
517
+
518
518
final Request req = httpTransactionContext .getAhcRequest ();
519
519
520
520
try {
@@ -526,11 +526,14 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
526
526
if (ntlmAuthenticate != null ) {
527
527
// NTLM
528
528
// Connection-based auth
529
- isContinueAuth = true ;
530
-
531
- newRealm = ntlmChallenge (ctx .getConnection (),
529
+ isContinueAuth = ntlmChallenge (ctx .getConnection (),
532
530
ntlmAuthenticate , req ,
533
531
req .getHeaders (), realm );
532
+
533
+ newRealm = new Realm .RealmBuilder ().clone (realm )//
534
+ .setUri (req .getUri ())//
535
+ .setMethodName (req .getMethod ())//
536
+ .build ();
534
537
} else {
535
538
// Request-based auth
536
539
isContinueAuth = false ;
@@ -539,7 +542,6 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
539
542
540
543
newRealm = new Realm .RealmBuilder ()
541
544
.clone (realm )
542
- .setScheme (realm .getScheme ())
543
545
.setUri (req .getUri ())
544
546
.setMethodName (req .getMethod ())
545
547
.setUsePreemptiveAuth (true )
@@ -564,7 +566,6 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
564
566
}
565
567
566
568
final Request nextRequest = new RequestBuilder (req )
567
- .setHeaders (req .getHeaders ())
568
569
.setRealm (newRealm )
569
570
.build ();
570
571
@@ -577,10 +578,8 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
577
578
578
579
try {
579
580
provider .execute (newContext );
580
- return false ;
581
581
} catch (IOException ioe ) {
582
582
newContext .abort (ioe );
583
- return false ;
584
583
}
585
584
} catch (Exception e ) {
586
585
httpTransactionContext .abort (e );
@@ -589,7 +588,7 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
589
588
return false ;
590
589
}
591
590
592
- private Realm ntlmChallenge (final Connection c ,
591
+ private boolean ntlmChallenge (final Connection c ,
593
592
final String wwwAuth , final Request request ,
594
593
final FluentCaseInsensitiveStringsMap headers ,
595
594
final Realm realm )
@@ -600,18 +599,16 @@ private Realm ntlmChallenge(final Connection c,
600
599
String challengeHeader = NTLMEngine .INSTANCE .generateType1Msg ();
601
600
602
601
addNTLMAuthorizationHeader (headers , challengeHeader , false );
602
+ return true ;
603
603
} else {
604
604
// probably receiving Type2Msg, so we issue Type3Msg
605
605
addType3NTLMAuthorizationHeader (wwwAuth , headers , realm , false );
606
606
// we mark NTLM as established for the Connection to
607
607
// avoid preemptive NTLM
608
608
Utils .setNtlmEstablished (c );
609
+
610
+ return false ;
609
611
}
610
-
611
- return new Realm .RealmBuilder ().clone (realm )//
612
- .setUri (request .getUri ())//
613
- .setMethodName (request .getMethod ())//
614
- .build ();
615
612
}
616
613
617
614
private void addNTLMAuthorizationHeader (
@@ -669,24 +666,18 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
669
666
if (redirectURL == null ) {
670
667
throw new IllegalStateException ("redirect received, but no location header was present" );
671
668
}
672
-
673
-
669
+
674
670
final Request req = httpTransactionContext .getAhcRequest ();
675
671
final GrizzlyAsyncHttpProvider provider = httpTransactionContext .provider ;
676
672
677
- final Uri orig = httpTransactionContext .lastRedirectURI == null
673
+ final Uri origUri = httpTransactionContext .lastRedirectUri == null
678
674
? req .getUri ()
679
- : Uri .create (req .getUri (),
680
- httpTransactionContext .lastRedirectURI );
675
+ : httpTransactionContext .lastRedirectUri ;
681
676
682
- httpTransactionContext .lastRedirectURI = redirectURL ;
683
- Request requestToSend ;
684
- Uri uri = Uri .create (orig , redirectURL );
685
- if (!uri .toUrl ().equalsIgnoreCase (orig .toUrl ())) {
686
- requestToSend = newRequest (uri , responsePacket ,
687
- httpTransactionContext ,
688
- sendAsGet (responsePacket , httpTransactionContext ));
689
- } else {
677
+ final Uri redirectUri = Uri .create (origUri , redirectURL );
678
+ httpTransactionContext .lastRedirectUri = redirectUri ;
679
+
680
+ if (redirectUri .equals (origUri )) {
690
681
httpTransactionContext .statusHandler = null ;
691
682
httpTransactionContext .invocationStatus = InvocationStatus .CONTINUE ;
692
683
try {
@@ -695,28 +686,49 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
695
686
} catch (Exception e ) {
696
687
httpTransactionContext .abort (e );
697
688
}
689
+
698
690
return true ;
699
691
}
700
- final ConnectionManager m = provider .getConnectionManager ();
692
+
693
+ final Request nextRequest = newRequest (httpTransactionContext ,
694
+ redirectUri , responsePacket ,
695
+ getRealm (httpTransactionContext ),
696
+ sendAsGet (responsePacket , httpTransactionContext ));
697
+
701
698
try {
702
- final Connection c = m .openSync (requestToSend );
703
- if (switchingSchemes (orig , uri )) {
704
- try {
705
- notifySchemeSwitch (ctx , c , uri );
706
- } catch (IOException ioe ) {
707
- httpTransactionContext .abort (ioe );
708
- }
699
+ responsePacket .setSkipRemainder (true ); // ignore the remainder of the response
700
+
701
+ final Connection c ;
702
+
703
+ // @TODO we may want to ditch the keep-alive connection if the response payload is too large
704
+ if (responsePacket .getProcessingState ().isKeepAlive () &&
705
+ isSameHostAndProtocol (origUri , redirectUri )) {
706
+ // if it's HTTP keep-alive connection - reuse the
707
+ // same Grizzly Connection
708
+ c = ctx .getConnection ();
709
+ httpTransactionContext .reuseConnection ();
710
+ } else {
711
+ // if it's not keep-alive - take new Connection from the pool
712
+ final ConnectionManager m = provider .getConnectionManager ();
713
+ c = m .openSync (nextRequest );
709
714
}
715
+
710
716
final HttpTransactionContext newContext =
711
717
httpTransactionContext .cloneAndStartTransactionFor (
712
- c , requestToSend );
718
+ c , nextRequest );
713
719
714
720
newContext .invocationStatus = InvocationStatus .CONTINUE ;
715
- provider .execute (newContext );
721
+ try {
722
+ provider .execute (newContext );
723
+ } catch (IOException ioe ) {
724
+ newContext .abort (ioe );
725
+ }
726
+
716
727
return false ;
717
728
} catch (Exception e ) {
718
729
httpTransactionContext .abort (e );
719
730
}
731
+
720
732
httpTransactionContext .invocationStatus = InvocationStatus .CONTINUE ;
721
733
return true ;
722
734
}
@@ -734,24 +746,48 @@ private boolean switchingSchemes(final Uri oldUri, final Uri newUri) {
734
746
}
735
747
736
748
private void notifySchemeSwitch (final FilterChainContext ctx ,
737
- final Connection c , final Uri uri ) throws IOException {
749
+ final Connection c , final Uri uri ) {
738
750
ctx .notifyDownstream (new SSLSwitchingEvent ("https" .equals (uri .getScheme ()), c ));
739
751
}
740
752
} // END RedirectHandler
753
+
741
754
742
755
// ----------------------------------------------------- Private Methods
743
- private static Request newRequest (final Uri uri ,
744
- final HttpResponsePacket response ,
745
- final HttpTransactionContext ctx , boolean asGet ) {
746
- final RequestBuilder builder = new RequestBuilder (ctx .getAhcRequest ());
756
+ private static Request newRequest (final HttpTransactionContext ctx ,
757
+ final Uri newUri , final HttpResponsePacket response ,
758
+ final Realm realm , boolean asGet ) {
759
+ final Request prototype = ctx .getAhcRequest ();
760
+ final FluentCaseInsensitiveStringsMap prototypeHeaders =
761
+ prototype .getHeaders ();
762
+
763
+ prototypeHeaders .remove (Header .Host .toString ());
764
+ prototypeHeaders .remove (Header .ContentLength .toString ());
765
+
766
+ if (asGet )
767
+ prototypeHeaders .remove (Header .ContentType .toString ());
768
+ if (realm != null && realm .getScheme () == AuthScheme .NTLM ) {
769
+ prototypeHeaders .remove (Header .Authorization .toString ());
770
+ prototypeHeaders .remove (Header .ProxyAuthorization .toString ());
771
+ }
772
+
773
+ final RequestBuilder builder = new RequestBuilder (prototype );
747
774
if (asGet ) {
748
775
builder .setMethod ("GET" );
749
776
}
750
- builder .setUrl (uri .toString ());
751
- for (String cookieStr : response .getHeaders ().values (Header .Cookie )) {
777
+ builder .setUrl (newUri .toString ());
778
+ for (String cookieStr : response .getHeaders ().values (Header .SetCookie )) {
752
779
builder .addOrReplaceCookie (CookieDecoder .decode (cookieStr ));
753
780
}
781
+
754
782
return builder .build ();
755
783
}
756
784
785
+ private static Realm getRealm (final HttpTransactionContext httpTransactionContext ) {
786
+ final Realm realm = httpTransactionContext .getAhcRequest ().getRealm ();
787
+
788
+ return realm != null
789
+ ? realm
790
+ : httpTransactionContext .provider .getClientConfig ().getRealm ();
791
+ }
792
+
757
793
} // END AsyncHttpClientEventFilter
0 commit comments