36
36
import androidx .media3 .common .util .Assertions ;
37
37
import androidx .media3 .common .util .Clock ;
38
38
import androidx .media3 .common .util .ConditionVariable ;
39
+ import androidx .media3 .common .util .Log ;
39
40
import androidx .media3 .common .util .UnstableApi ;
40
41
import androidx .media3 .common .util .Util ;
41
42
import com .google .common .base .Ascii ;
42
43
import com .google .common .base .Predicate ;
44
+ import com .google .common .collect .ImmutableMap ;
43
45
import com .google .common .net .HttpHeaders ;
44
46
import com .google .common .primitives .Longs ;
45
47
import com .google .errorprone .annotations .CanIgnoreReturnValue ;
46
48
import java .io .IOException ;
47
49
import java .io .InterruptedIOException ;
50
+ import java .net .CookieHandler ;
51
+ import java .net .CookieManager ;
48
52
import java .net .SocketTimeoutException ;
53
+ import java .net .URI ;
49
54
import java .net .UnknownHostException ;
50
55
import java .nio .ByteBuffer ;
51
56
import java .util .Arrays ;
@@ -321,6 +326,8 @@ public OpenException(
321
326
// The size of read buffer passed to cronet UrlRequest.read().
322
327
private static final int READ_BUFFER_SIZE_BYTES = 32 * 1024 ;
323
328
329
+ private static final String TAG = "HttpEngineDataSource" ;
330
+
324
331
private final HttpEngine httpEngine ;
325
332
private final Executor executor ;
326
333
private final int requestPriority ;
@@ -709,7 +716,7 @@ public synchronized void close() {
709
716
@ UnstableApi
710
717
@ VisibleForTesting
711
718
@ Nullable
712
- UrlRequest . Callback getCurrentUrlRequestCallback () {
719
+ UrlRequestCallback getCurrentUrlRequestCallback () {
713
720
return currentUrlRequestWrapper == null
714
721
? null
715
722
: currentUrlRequestWrapper .getUrlRequestCallback ();
@@ -932,14 +939,6 @@ private static boolean isCompressed(UrlResponseInfo info) {
932
939
return false ;
933
940
}
934
941
935
- @ Nullable
936
- private static String parseCookies (@ Nullable List <String > setCookieHeaders ) {
937
- if (setCookieHeaders == null || setCookieHeaders .isEmpty ()) {
938
- return null ;
939
- }
940
- return TextUtils .join (";" , setCookieHeaders );
941
- }
942
-
943
942
@ Nullable
944
943
private static String getFirstHeader (Map <String , List <String >> allHeaders , String headerName ) {
945
944
@ Nullable List <String > headers = allHeaders .get (headerName );
@@ -957,6 +956,55 @@ private static int copyByteBuffer(ByteBuffer src, ByteBuffer dst) {
957
956
return remaining ;
958
957
}
959
958
959
+ /**
960
+ * Stores the cookie headers from the response in the default {@link CookieHandler}.
961
+ *
962
+ * This is a no-op if the {@link CookieHandler} is not set .
963
+ */
964
+ @ VisibleForTesting
965
+ /* private */ static void storeCookiesFromHeaders (UrlResponseInfo info ) {
966
+ CookieHandler cookieHandler = CookieHandler .getDefault ();
967
+ if (cookieHandler != null ) {
968
+ try {
969
+ cookieHandler .put (new URI (info .getUrl ()), info .getHeaders ().getAsMap ());
970
+ } catch (Exception e ) {
971
+ Log .w (TAG , "Failed to store cookies in CookieHandler" , e );
972
+ }
973
+ }
974
+ }
975
+
976
+ @ VisibleForTesting
977
+ /* private */ static String getCookieHeader (String url ) {
978
+ return getCookieHeader (url , ImmutableMap .of ());
979
+ }
980
+
981
+ // getCookieHeader maps Set-Cookie2 (RFC 2965) to Cookie just like CookieManager does.
982
+ @ VisibleForTesting
983
+ /* private */ static String getCookieHeader (String url , Map <String , List <String >> headers ) {
984
+ CookieHandler cookieHandler = CookieHandler .getDefault ();
985
+ if (cookieHandler == null ) {
986
+ return "" ;
987
+ }
988
+
989
+ Map <String , List <String >> cookieHeaders = ImmutableMap .of ();
990
+ try {
991
+ cookieHeaders = cookieHandler .get (new URI (url ), headers );
992
+ } catch (Exception e ) {
993
+ Log .w (TAG , "Failed to read cookies from CookieHandler" , e );
994
+ }
995
+
996
+ StringBuilder cookies = new StringBuilder ();
997
+ if (cookieHeaders .containsKey (HttpHeaders .COOKIE )) {
998
+ List <String > cookiesList = cookieHeaders .get (HttpHeaders .COOKIE );
999
+ if (cookiesList != null ) {
1000
+ for (String cookie : cookiesList ) {
1001
+ cookies .append (cookie ).append ("; " );
1002
+ }
1003
+ }
1004
+ }
1005
+ return cookies .toString ().stripTrailing ();
1006
+ }
1007
+
960
1008
/**
961
1009
* A wrapper class that manages a {@link UrlRequest} and the {@link UrlRequestCallback} associated
962
1010
* with that request.
@@ -984,7 +1032,7 @@ public void close() {
984
1032
urlRequest .cancel ();
985
1033
}
986
1034
987
- public UrlRequest . Callback getUrlRequestCallback () {
1035
+ public UrlRequestCallback getUrlRequestCallback () {
988
1036
return urlRequestCallback ;
989
1037
}
990
1038
@@ -1004,14 +1052,18 @@ public void onStatus(int status) {
1004
1052
}
1005
1053
}
1006
1054
1007
- private final class UrlRequestCallback implements UrlRequest .Callback {
1008
-
1055
+ final class UrlRequestCallback implements UrlRequest .Callback {
1009
1056
private volatile boolean isClosed = false ;
1010
1057
1011
1058
public void close () {
1012
1059
this .isClosed = true ;
1013
1060
}
1014
1061
1062
+ @ SuppressWarnings ("argument.type.incompatible" )
1063
+ private void resetDefaultCookieHandler () {
1064
+ CookieHandler .setDefault (null );
1065
+ }
1066
+
1015
1067
@ Override
1016
1068
public synchronized void onRedirectReceived (
1017
1069
UrlRequest request , UrlResponseInfo info , String newLocationUrl ) {
@@ -1040,24 +1092,34 @@ public synchronized void onRedirectReceived(
1040
1092
resetConnectTimeout ();
1041
1093
}
1042
1094
1095
+ boolean hasDefaultCookieHandler = CookieHandler .getDefault () != null ;
1096
+ if (!hasDefaultCookieHandler && handleSetCookieRequests ) {
1097
+ // a temporary CookieManager is created for the duration of this request - this guarantees
1098
+ // redirects preserve the cookies correctly.
1099
+ CookieManager cm = new CookieManager ();
1100
+ CookieHandler .setDefault (cm );
1101
+ }
1102
+
1103
+ storeCookiesFromHeaders (info );
1104
+ String cookieHeaders = getCookieHeader (info .getUrl (), info .getHeaders ().getAsMap ());
1105
+
1106
+ if (!hasDefaultCookieHandler && handleSetCookieRequests ) {
1107
+ resetDefaultCookieHandler ();
1108
+ }
1109
+
1043
1110
boolean shouldKeepPost =
1044
1111
keepPostFor302Redirects
1045
1112
&& dataSpec .httpMethod == DataSpec .HTTP_METHOD_POST
1046
1113
&& responseCode == 302 ;
1047
1114
1048
1115
// request.followRedirect() transforms a POST request into a GET request, so if we want to
1049
1116
// keep it as a POST we need to fall through to the manual redirect logic below.
1050
- if (!shouldKeepPost && !handleSetCookieRequests ) {
1051
- request .followRedirect ();
1052
- return ;
1053
- }
1054
-
1055
- @ Nullable
1056
- String cookieHeadersValue =
1057
- parseCookies (info .getHeaders ().getAsMap ().get (HttpHeaders .SET_COOKIE ));
1058
- if (!shouldKeepPost && TextUtils .isEmpty (cookieHeadersValue )) {
1059
- request .followRedirect ();
1060
- return ;
1117
+ if (!shouldKeepPost ) {
1118
+ // No cookies, or we're not handling them - so just follow the redirect.
1119
+ if (!handleSetCookieRequests || TextUtils .isEmpty (cookieHeaders )) {
1120
+ request .followRedirect ();
1121
+ return ;
1122
+ }
1061
1123
}
1062
1124
1063
1125
request .cancel ();
@@ -1075,13 +1137,15 @@ public synchronized void onRedirectReceived(
1075
1137
} else {
1076
1138
redirectUrlDataSpec = dataSpec .withUri (Uri .parse (newLocationUrl ));
1077
1139
}
1078
- if (!TextUtils .isEmpty (cookieHeadersValue )) {
1140
+
1141
+ if (!TextUtils .isEmpty (cookieHeaders )) {
1079
1142
Map <String , String > requestHeaders = new HashMap <>();
1080
1143
requestHeaders .putAll (dataSpec .httpRequestHeaders );
1081
- requestHeaders .put (HttpHeaders .COOKIE , cookieHeadersValue );
1144
+ requestHeaders .put (HttpHeaders .COOKIE , cookieHeaders );
1082
1145
redirectUrlDataSpec =
1083
1146
redirectUrlDataSpec .buildUpon ().setHttpRequestHeaders (requestHeaders ).build ();
1084
1147
}
1148
+
1085
1149
UrlRequestWrapper redirectUrlRequestWrapper ;
1086
1150
try {
1087
1151
redirectUrlRequestWrapper = buildRequestWrapper (redirectUrlDataSpec );
@@ -1101,6 +1165,7 @@ public synchronized void onResponseStarted(UrlRequest request, UrlResponseInfo i
1101
1165
if (isClosed ) {
1102
1166
return ;
1103
1167
}
1168
+ storeCookiesFromHeaders (info );
1104
1169
responseInfo = info ;
1105
1170
operation .open ();
1106
1171
}
0 commit comments