5959import com .ning .http .util .AsyncHttpProviderUtils ;
6060import com .ning .http .util .AuthenticatorUtils ;
6161import static com .ning .http .util .MiscUtil .isNonEmpty ;
62- import static com .ning .http .util .MiscUtil .isNonEmpty ;
63- import static com .ning .http .util .MiscUtil .isNonEmpty ;
6462
6563import com .ning .http .util .ProxyUtils ;
6664import com .ning .http .util .SslUtils ;
@@ -561,18 +559,26 @@ static int getPort(final URI uri, final int p) {
561559
562560
563561@ SuppressWarnings ({"unchecked" })
564- boolean sendRequest (final FilterChainContext ctx ,
565- final Request request ,
562+ boolean sendRequest (final HttpTransactionContext httpCtx ,
563+ final FilterChainContext ctx ,
566564final HttpRequestPacket requestPacket ,
567565final BodyHandler bodyHandler )
568566throws IOException {
567+
568+ final Request request =httpCtx .request ;
569+ final AsyncHandler h =httpCtx .handler ;
570+ if (h instanceof TransferCompletionHandler ) {
571+ final FluentCaseInsensitiveStringsMap map =
572+ new FluentCaseInsensitiveStringsMap (request .getHeaders ());
573+ TransferCompletionHandler .class .cast (h ).transferAdapter (new GrizzlyTransferAdapter (map ));
574+ }
569575
576+ requestPacket .setConnection (ctx .getConnection ());
577+
570578boolean isWriteComplete =true ;
571579
572580if (bodyHandler !=null ) {// Check if the HTTP request has body
573- final HttpTransactionContext context =HttpTransactionContext .get (ctx .getConnection ());
574-
575- context .bodyHandler =bodyHandler ;
581+ httpCtx .bodyHandler =bodyHandler ;
576582if (LOGGER .isDebugEnabled ()) {
577583LOGGER .debug ("REQUEST: " +requestPacket .toString ());
578584 }
@@ -867,42 +873,36 @@ private boolean sendAsGrizzlyRequest(final Request request,
867873convertToUpgradeRequest (httpCtx );
868874 }
869875final Request req =httpCtx .request ;
870- final URI uri =req .isUseRawUrl () ?req .getRawURI () :req .getURI ();
871876final Method method =Method .valueOf (request .getMethod ());
872- final HttpRequestPacket .Builder builder =HttpRequestPacket .builder ();
877+ final URI uri =req .isUseRawUrl () ?req .getRawURI () :req .getURI ();
878+
873879boolean secure ="https" .equals (uri .getScheme ());
874- builder .method (method );
875- builder .protocol (Protocol .HTTP_1_1 );
876880
877- if (!request .getHeaders ().containsKey (Header .Host .toString ())) {
878- String host =request .getVirtualHost ();
879- if (host !=null ) {
880- builder .header (Header .Host ,host );
881- }else {
882- if (uri .getPort () == -1 ) {
883- builder .header (Header .Host ,uri .getHost ());
884- }else {
885- builder .header (Header .Host ,uri .getHost () +':' +uri .getPort ());
886- }
887- }
888- }
889881final ProxyServer proxy =ProxyUtils .getProxyServer (config ,request );
890882final boolean useProxy =proxy !=null ;
883+
884+ final boolean isEstablishingConnectTunnel =
885+ useProxy && (secure ||httpCtx .isWSRequest ) &&
886+ !httpCtx .isTunnelEstablished (connection );
891887
888+ if (isEstablishingConnectTunnel ) {
889+ // once the tunnel is established, sendAsGrizzlyRequest will
890+ // be called again and we'll finally send the request over the tunnel
891+ return establishConnectTunnel (proxy ,httpCtx ,uri ,ctx );
892+ }
893+
894+ final HttpRequestPacket .Builder builder =
895+ HttpRequestPacket .builder ()
896+ .protocol (Protocol .HTTP_1_1 )
897+ .method (method );
898+
892899if (useProxy ) {
893- if (secure ||httpCtx .isWSRequest ) {// TUNNELING?
894- if (!httpCtx .isTunnelEstablished (connection )) {
895- secure =false ;
896- httpCtx .establishingTunnel =true ;
897- builder .method (Method .CONNECT );
898- builder .uri (AsyncHttpProviderUtils .getAuthority (uri ));
900+ if (secure ||httpCtx .isWSRequest ) {// Sending message over established CONNECT tunnel
901+ if (config .isUseRelativeURIsWithConnectProxies ()) {
902+ builder .uri (uri .getRawPath ());
903+ builder .query (uri .getRawQuery ());
899904 }else {
900- if (config .isUseRelativeURIsWithConnectProxies ()) {
901- builder .uri (uri .getRawPath ());
902- builder .query (uri .getRawQuery ());
903- }else {
904- builder .uri (uri .toString ());
905- }
905+ builder .uri (uri .toString ());
906906 }
907907 }else {
908908builder .uri (uri .toString ());
@@ -912,10 +912,11 @@ private boolean sendAsGrizzlyRequest(final Request request,
912912builder .query (uri .getRawQuery ());
913913 }
914914
915- final BodyHandler bodyHandler =isPayloadAllowed (method ) ?
916- bodyHandlerFactory .getBodyHandler (request ) :
917- null ;
918-
915+ HttpRequestPacket requestPacket ;
916+ final BodyHandler bodyHandler =isPayloadAllowed (method )
917+ ?bodyHandlerFactory .getBodyHandler (request )
918+ :null ;
919+
919920if (bodyHandler !=null ) {
920921final long contentLength =request .getContentLength ();
921922if (contentLength >=0 ) {
@@ -925,53 +926,68 @@ private boolean sendAsGrizzlyRequest(final Request request,
925926builder .chunked (true );
926927 }
927928 }
928-
929- HttpRequestPacket requestPacket ;
930- if (httpCtx .isWSRequest && !httpCtx .establishingTunnel ) {
929+
930+ if (httpCtx .isWSRequest ) {
931931try {
932932final URI wsURI =new URI (httpCtx .wsRequestURI );
933933secure ="wss" .equalsIgnoreCase (wsURI .getScheme ());
934934httpCtx .protocolHandler =Version .RFC6455 .createHandler (true );
935935httpCtx .handshake =httpCtx .protocolHandler .createHandShake (wsURI );
936- requestPacket = (HttpRequestPacket )
937- httpCtx .handshake .composeHeaders ().getHttpHeader ();
936+ requestPacket = (HttpRequestPacket )httpCtx .handshake .composeHeaders ().getHttpHeader ();
938937 }catch (URISyntaxException e ) {
939938throw new IllegalArgumentException ("Invalid WS URI: " +httpCtx .wsRequestURI );
940939 }
941940 }else {
942941requestPacket =builder .build ();
943942 }
944- requestPacket .setSecure (secure );
945943
944+ requestPacket .setSecure (secure );
946945ctx .notifyDownstream (new SwitchingSSLFilter .SSLSwitchingEvent (secure ,connection ));
947946
948- addHeaders (request ,requestPacket );
947+ copyHeaders (request ,requestPacket );
949948addCookies (request ,requestPacket );
950- addAuthorizationHeader (request ,requestPacket );
951949
952- if (useProxy ) {
953- if (!requestPacket .getHeaders ().contains (Header .ProxyConnection )) {
954- requestPacket .setHeader (Header .ProxyConnection ,"keep-alive" );
955- }
950+ addServiceHeaders (requestPacket );
951+ addHostHeaderIfNeeded (request ,uri ,requestPacket );
952+ addAuthorizationHeader (getRealm (request ),requestPacket );
956953
957- if (proxy .getPrincipal () !=null ) {
958- requestPacket .setHeader (Header .ProxyAuthorization ,
959- AuthenticatorUtils .computeBasicAuthentication (proxy ));
960- }
961- }
962- final AsyncHandler h =httpCtx .handler ;
963- if (h instanceof TransferCompletionHandler ) {
964- final FluentCaseInsensitiveStringsMap map =
965- new FluentCaseInsensitiveStringsMap (request .getHeaders ());
966- TransferCompletionHandler .class .cast (h ).transferAdapter (new GrizzlyTransferAdapter (map ));
954+ if (useProxy ) {
955+ addProxyHeaders (proxy ,requestPacket );
967956 }
968957
969- requestPacket .setConnection (connection );
970- return sendRequest (ctx ,request ,requestPacket ,
958+ return sendRequest (httpCtx ,ctx ,requestPacket ,
971959wrapWithExpectHandlerIfNeeded (bodyHandler ,requestPacket ));
972960
973961 }
974962
963+ private boolean establishConnectTunnel (
964+ final ProxyServer proxy ,
965+ final HttpTransactionContext httpCtx ,
966+ final URI uri ,
967+ final FilterChainContext ctx )throws IOException {
968+ final Connection connection =ctx .getConnection ();
969+
970+ final HttpRequestPacket requestPacket =
971+ HttpRequestPacket .builder ()
972+ .protocol (Protocol .HTTP_1_0 )
973+ .method (Method .CONNECT )
974+ .uri (AsyncHttpProviderUtils .getAuthority (uri ))
975+ .build ();
976+
977+ httpCtx .establishingTunnel =true ;
978+
979+ // turn off SSL, because CONNECT will be sent in plain mode
980+ ctx .notifyDownstream (new SwitchingSSLFilter .SSLSwitchingEvent (false ,connection ));
981+
982+ final Request request =httpCtx .request ;
983+ addServiceHeaders (requestPacket );
984+ addHostHeaderIfNeeded (request ,uri ,requestPacket );
985+ addAuthorizationHeader (getRealm (request ),requestPacket );
986+ addProxyHeaders (proxy ,requestPacket );
987+
988+ return sendRequest (httpCtx ,ctx ,requestPacket ,null );
989+ }
990+
975991/**
976992 * check if we need to wrap the BodyHandler with ExpectHandler
977993 */
@@ -997,11 +1013,7 @@ private boolean isPayloadAllowed(final Method method) {
9971013return method .getPayloadExpectation () !=Method .PayloadExpectation .NOT_ALLOWED ;
9981014 }
9991015
1000- private void addAuthorizationHeader (final Request request ,final HttpRequestPacket requestPacket ) {
1001- Realm realm =request .getRealm ();
1002- if (realm ==null ) {
1003- realm =config .getRealm ();
1004- }
1016+ private void addAuthorizationHeader (final Realm realm ,final HttpRequestPacket requestPacket ) {
10051017if (realm !=null &&realm .getUsePreemptiveAuth ()) {
10061018final String authHeaderValue =generateAuthHeader (realm );
10071019if (authHeaderValue !=null ) {
@@ -1010,6 +1022,40 @@ private void addAuthorizationHeader(final Request request, final HttpRequestPack
10101022 }
10111023 }
10121024
1025+ private void addProxyHeaders (final ProxyServer proxy ,
1026+ final HttpRequestPacket requestPacket )
1027+ throws UnsupportedEncodingException {
1028+
1029+ if (!requestPacket .getHeaders ().contains (Header .ProxyConnection )) {
1030+ requestPacket .setHeader (Header .ProxyConnection ,"keep-alive" );
1031+ }
1032+
1033+ if (proxy .getPrincipal () !=null ) {
1034+ requestPacket .setHeader (Header .ProxyAuthorization ,
1035+ AuthenticatorUtils .computeBasicAuthentication (proxy ));
1036+ }
1037+ }
1038+
1039+ private void addHostHeaderIfNeeded (final Request request ,
1040+ final URI uri ,final HttpRequestPacket requestPacket ) {
1041+ if (!requestPacket .containsHeader (Header .Host )) {
1042+ String host =request .getVirtualHost ();
1043+ if (host !=null ) {
1044+ requestPacket .addHeader (Header .Host ,host );
1045+ }else {
1046+ if (uri .getPort () == -1 ) {
1047+ requestPacket .addHeader (Header .Host ,uri .getHost ());
1048+ }else {
1049+ requestPacket .addHeader (Header .Host ,uri .getHost () +':' +uri .getPort ());
1050+ }
1051+ }
1052+ }
1053+ }
1054+
1055+ private Realm getRealm (final Request request ) {
1056+ return request .getRealm () !=null ?request .getRealm () :config .getRealm ();
1057+ }
1058+
10131059private String generateAuthHeader (final Realm realm ) {
10141060try {
10151061switch (realm .getAuthScheme ()) {
@@ -1051,7 +1097,7 @@ private void convertToUpgradeRequest(final HttpTransactionContext ctx) {
10511097ctx .requestUrl =sb .toString ();
10521098 }
10531099
1054- private void addHeaders (final Request request ,
1100+ private void copyHeaders (final Request request ,
10551101final HttpRequestPacket requestPacket ) {
10561102
10571103final FluentCaseInsensitiveStringsMap map =request .getHeaders ();
@@ -1066,7 +1112,9 @@ private void addHeaders(final Request request,
10661112 }
10671113 }
10681114 }
1069-
1115+ }
1116+
1117+ private void addServiceHeaders (final HttpRequestPacket requestPacket ) {
10701118final MimeHeaders headers =requestPacket .getHeaders ();
10711119if (!headers .contains (Header .Connection )) {
10721120requestPacket .addHeader (Header .Connection ,"keep-alive" );
@@ -1079,8 +1127,6 @@ private void addHeaders(final Request request,
10791127if (!headers .contains (Header .UserAgent )) {
10801128requestPacket .addHeader (Header .UserAgent ,config .getUserAgent ());
10811129 }
1082-
1083-
10841130 }
10851131
10861132