Fix SocketTimeoutException in CronetDataSource
.
In some versions of Cronet, the request provided by the callback is not always the same. This leads to callback not completing and request timing out. The fix changes from having one `UrlRequestCallback` per `CronetDataSource` to one `UrlRequestCallback` per `UrlRequest`. Everytime a current request is canceled, the current callback is closed. PiperOrigin-RevId: 627379153
This commit is contained in:
parent
b813bd3976
commit
f2276f613d
@ -46,6 +46,10 @@
|
|||||||
* Downloads:
|
* Downloads:
|
||||||
* OkHttp Extension:
|
* OkHttp Extension:
|
||||||
* Cronet Extension:
|
* Cronet Extension:
|
||||||
|
* Fix `SocketTimeoutException` in `CronetDataSource`. In some versions of
|
||||||
|
Cronet, the request provided by the callback is not always the same.
|
||||||
|
This leads to callback not completing and request timing out
|
||||||
|
(https://issuetracker.google.com/328442628).
|
||||||
* RTMP Extension:
|
* RTMP Extension:
|
||||||
* HLS Extension:
|
* HLS Extension:
|
||||||
* Fix bug where pending EMSG samples waiting for a discontinuity were
|
* Fix bug where pending EMSG samples waiting for a discontinuity were
|
||||||
|
@ -22,6 +22,7 @@ import static org.chromium.net.UrlRequest.Builder.REQUEST_PRIORITY_MEDIUM;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaLibraryInfo;
|
import androidx.media3.common.MediaLibraryInfo;
|
||||||
import androidx.media3.common.PlaybackException;
|
import androidx.media3.common.PlaybackException;
|
||||||
@ -55,6 +56,9 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
|
import org.checkerframework.dataflow.qual.SideEffectFree;
|
||||||
import org.chromium.net.CronetEngine;
|
import org.chromium.net.CronetEngine;
|
||||||
import org.chromium.net.CronetException;
|
import org.chromium.net.CronetException;
|
||||||
import org.chromium.net.NetworkException;
|
import org.chromium.net.NetworkException;
|
||||||
@ -441,8 +445,6 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
// The size of read buffer passed to cronet UrlRequest.read().
|
// The size of read buffer passed to cronet UrlRequest.read().
|
||||||
private static final int DEFAULT_READ_BUFFER_SIZE_BYTES = 32 * 1024;
|
private static final int DEFAULT_READ_BUFFER_SIZE_BYTES = 32 * 1024;
|
||||||
|
|
||||||
/* package */ final UrlRequest.Callback urlRequestCallback;
|
|
||||||
|
|
||||||
private final CronetEngine cronetEngine;
|
private final CronetEngine cronetEngine;
|
||||||
private final Executor executor;
|
private final Executor executor;
|
||||||
private final int requestPriority;
|
private final int requestPriority;
|
||||||
@ -467,6 +469,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
// Written from the calling thread only. currentUrlRequest.start() calls ensure writes are visible
|
// Written from the calling thread only. currentUrlRequest.start() calls ensure writes are visible
|
||||||
// to reads made by the Cronet thread.
|
// to reads made by the Cronet thread.
|
||||||
@Nullable private UrlRequest currentUrlRequest;
|
@Nullable private UrlRequest currentUrlRequest;
|
||||||
|
@VisibleForTesting @Nullable /* package */ UrlRequestCallback currentUrlRequestCallback;
|
||||||
@Nullable private DataSpec currentDataSpec;
|
@Nullable private DataSpec currentDataSpec;
|
||||||
|
|
||||||
// Reference written and read by calling thread only. Passed to Cronet thread as a local variable.
|
// Reference written and read by calling thread only. Passed to Cronet thread as a local variable.
|
||||||
@ -510,7 +513,6 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
this.keepPostFor302Redirects = keepPostFor302Redirects;
|
this.keepPostFor302Redirects = keepPostFor302Redirects;
|
||||||
clock = Clock.DEFAULT;
|
clock = Clock.DEFAULT;
|
||||||
this.readBufferSize = readBufferSize;
|
this.readBufferSize = readBufferSize;
|
||||||
urlRequestCallback = new UrlRequestCallback();
|
|
||||||
requestProperties = new RequestProperties();
|
requestProperties = new RequestProperties();
|
||||||
operation = new ConditionVariable();
|
operation = new ConditionVariable();
|
||||||
}
|
}
|
||||||
@ -576,8 +578,8 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
currentDataSpec = dataSpec;
|
currentDataSpec = dataSpec;
|
||||||
UrlRequest urlRequest;
|
UrlRequest urlRequest;
|
||||||
try {
|
try {
|
||||||
urlRequest = buildRequestBuilder(dataSpec).build();
|
createCurrentUrlRequestAndCallback(dataSpec);
|
||||||
currentUrlRequest = urlRequest;
|
urlRequest = currentUrlRequest;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (e instanceof HttpDataSourceException) {
|
if (e instanceof HttpDataSourceException) {
|
||||||
throw (HttpDataSourceException) e;
|
throw (HttpDataSourceException) e;
|
||||||
@ -817,10 +819,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public synchronized void close() {
|
public synchronized void close() {
|
||||||
if (currentUrlRequest != null) {
|
closeCurrentUrlRequestAndCallback();
|
||||||
currentUrlRequest.cancel();
|
|
||||||
currentUrlRequest = null;
|
|
||||||
}
|
|
||||||
if (readBuffer != null) {
|
if (readBuffer != null) {
|
||||||
readBuffer.limit(0);
|
readBuffer.limit(0);
|
||||||
}
|
}
|
||||||
@ -834,6 +833,18 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void closeCurrentUrlRequestAndCallback() {
|
||||||
|
if (currentUrlRequest != null) {
|
||||||
|
currentUrlRequest.cancel();
|
||||||
|
currentUrlRequest = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUrlRequestCallback != null) {
|
||||||
|
currentUrlRequestCallback.close();
|
||||||
|
currentUrlRequestCallback = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns current {@link UrlRequest}. May be null if the data source is not opened. */
|
/** Returns current {@link UrlRequest}. May be null if the data source is not opened. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -848,11 +859,26 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
return responseInfo;
|
return responseInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The nullness checker can't prove that UrlRequest.Builder.build() doesn't null out
|
||||||
|
// this.currentUrlRequestCallback.
|
||||||
|
// TODO: Add @SideEffectFree to the UrlRequest.Builder.build() stub
|
||||||
|
@SuppressWarnings("nullness:contracts.postcondition.not.satisfied")
|
||||||
|
@EnsuresNonNull({"this.currentUrlRequestCallback", "this.currentUrlRequest"})
|
||||||
|
private void createCurrentUrlRequestAndCallback(DataSpec dataSpec) throws IOException {
|
||||||
|
currentUrlRequestCallback = new UrlRequestCallback();
|
||||||
|
currentUrlRequest = buildRequestBuilder(dataSpec).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@link UrlRequest.Builder} from dataSpec. Would not work if data source is not opened.
|
||||||
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
|
@SideEffectFree
|
||||||
|
@RequiresNonNull("this.currentUrlRequestCallback")
|
||||||
protected UrlRequest.Builder buildRequestBuilder(DataSpec dataSpec) throws IOException {
|
protected UrlRequest.Builder buildRequestBuilder(DataSpec dataSpec) throws IOException {
|
||||||
UrlRequest.Builder requestBuilder =
|
UrlRequest.Builder requestBuilder =
|
||||||
cronetEngine
|
cronetEngine
|
||||||
.newUrlRequestBuilder(dataSpec.uri.toString(), urlRequestCallback, executor)
|
.newUrlRequestBuilder(dataSpec.uri.toString(), currentUrlRequestCallback, executor)
|
||||||
.setPriority(requestPriority)
|
.setPriority(requestPriority)
|
||||||
.allowDirectExecutor();
|
.allowDirectExecutor();
|
||||||
|
|
||||||
@ -1068,13 +1094,6 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
return TextUtils.join(";", setCookieHeaders);
|
return TextUtils.join(";", setCookieHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void attachCookies(UrlRequest.Builder requestBuilder, @Nullable String cookies) {
|
|
||||||
if (TextUtils.isEmpty(cookies)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
requestBuilder.addHeader(HttpHeaders.COOKIE, cookies);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getStatus(UrlRequest request) throws InterruptedException {
|
private static int getStatus(UrlRequest request) throws InterruptedException {
|
||||||
final ConditionVariable conditionVariable = new ConditionVariable();
|
final ConditionVariable conditionVariable = new ConditionVariable();
|
||||||
final int[] statusHolder = new int[1];
|
final int[] statusHolder = new int[1];
|
||||||
@ -1107,15 +1126,23 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
return remaining;
|
return remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class UrlRequestCallback extends UrlRequest.Callback {
|
@VisibleForTesting
|
||||||
|
/* package */ final class UrlRequestCallback extends UrlRequest.Callback {
|
||||||
|
|
||||||
|
private volatile boolean isClosed = false;
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
this.isClosed = true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void onRedirectReceived(
|
public synchronized void onRedirectReceived(
|
||||||
UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
|
UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
|
||||||
if (request != currentUrlRequest) {
|
if (isClosed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UrlRequest urlRequest = Assertions.checkNotNull(currentUrlRequest);
|
Assertions.checkNotNull(currentUrlRequest);
|
||||||
|
Assertions.checkNotNull(currentUrlRequestCallback);
|
||||||
DataSpec dataSpec = Assertions.checkNotNull(currentDataSpec);
|
DataSpec dataSpec = Assertions.checkNotNull(currentDataSpec);
|
||||||
int responseCode = info.getHttpStatusCode();
|
int responseCode = info.getHttpStatusCode();
|
||||||
if (dataSpec.httpMethod == DataSpec.HTTP_METHOD_POST) {
|
if (dataSpec.httpMethod == DataSpec.HTTP_METHOD_POST) {
|
||||||
@ -1156,7 +1183,6 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
urlRequest.cancel();
|
|
||||||
DataSpec redirectUrlDataSpec;
|
DataSpec redirectUrlDataSpec;
|
||||||
if (!shouldKeepPost && dataSpec.httpMethod == DataSpec.HTTP_METHOD_POST) {
|
if (!shouldKeepPost && dataSpec.httpMethod == DataSpec.HTTP_METHOD_POST) {
|
||||||
// For POST redirects that aren't 307 or 308, the redirect is followed but request is
|
// For POST redirects that aren't 307 or 308, the redirect is followed but request is
|
||||||
@ -1171,21 +1197,29 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
} else {
|
} else {
|
||||||
redirectUrlDataSpec = dataSpec.withUri(Uri.parse(newLocationUrl));
|
redirectUrlDataSpec = dataSpec.withUri(Uri.parse(newLocationUrl));
|
||||||
}
|
}
|
||||||
UrlRequest.Builder requestBuilder;
|
|
||||||
|
if (!TextUtils.isEmpty(cookieHeadersValue)) {
|
||||||
|
Map<String, String> requestHeaders = new HashMap<>();
|
||||||
|
requestHeaders.putAll(dataSpec.httpRequestHeaders);
|
||||||
|
requestHeaders.put(HttpHeaders.COOKIE, cookieHeadersValue);
|
||||||
|
redirectUrlDataSpec =
|
||||||
|
redirectUrlDataSpec.buildUpon().setHttpRequestHeaders(requestHeaders).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
closeCurrentUrlRequestAndCallback();
|
||||||
try {
|
try {
|
||||||
requestBuilder = buildRequestBuilder(redirectUrlDataSpec);
|
createCurrentUrlRequestAndCallback(redirectUrlDataSpec);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
exception = e;
|
exception = e;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
attachCookies(requestBuilder, cookieHeadersValue);
|
|
||||||
currentUrlRequest = requestBuilder.build();
|
|
||||||
currentUrlRequest.start();
|
currentUrlRequest.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
|
public synchronized void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
|
||||||
if (request != currentUrlRequest) {
|
if (isClosed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
responseInfo = info;
|
responseInfo = info;
|
||||||
@ -1195,7 +1229,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
@Override
|
@Override
|
||||||
public synchronized void onReadCompleted(
|
public synchronized void onReadCompleted(
|
||||||
UrlRequest request, UrlResponseInfo info, ByteBuffer buffer) {
|
UrlRequest request, UrlResponseInfo info, ByteBuffer buffer) {
|
||||||
if (request != currentUrlRequest) {
|
if (isClosed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
operation.open();
|
operation.open();
|
||||||
@ -1203,7 +1237,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void onSucceeded(UrlRequest request, UrlResponseInfo info) {
|
public synchronized void onSucceeded(UrlRequest request, UrlResponseInfo info) {
|
||||||
if (request != currentUrlRequest) {
|
if (isClosed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
finished = true;
|
finished = true;
|
||||||
@ -1213,7 +1247,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
@Override
|
@Override
|
||||||
public synchronized void onFailed(
|
public synchronized void onFailed(
|
||||||
UrlRequest request, UrlResponseInfo info, CronetException error) {
|
UrlRequest request, UrlResponseInfo info, CronetException error) {
|
||||||
if (request != currentUrlRequest) {
|
if (isClosed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (error instanceof NetworkException
|
if (error instanceof NetworkException
|
||||||
|
@ -43,6 +43,7 @@ import androidx.media3.datasource.HttpDataSource;
|
|||||||
import androidx.media3.datasource.HttpDataSource.HttpDataSourceException;
|
import androidx.media3.datasource.HttpDataSource.HttpDataSourceException;
|
||||||
import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException;
|
import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException;
|
||||||
import androidx.media3.datasource.TransferListener;
|
import androidx.media3.datasource.TransferListener;
|
||||||
|
import androidx.media3.datasource.cronet.CronetDataSource.UrlRequestCallback;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InterruptedIOException;
|
import java.io.InterruptedIOException;
|
||||||
@ -234,6 +235,7 @@ public final class CronetDataSourceTest {
|
|||||||
mockResponseStartSuccess();
|
mockResponseStartSuccess();
|
||||||
|
|
||||||
dataSourceUnderTest.open(testDataSpec);
|
dataSourceUnderTest.open(testDataSpec);
|
||||||
|
UrlRequestCallback previousRequestCallback = dataSourceUnderTest.currentUrlRequestCallback;
|
||||||
dataSourceUnderTest.close();
|
dataSourceUnderTest.close();
|
||||||
// Prepare a mock UrlRequest to be used in the second open() call.
|
// Prepare a mock UrlRequest to be used in the second open() call.
|
||||||
final UrlRequest mockUrlRequest2 = mock(UrlRequest.class);
|
final UrlRequest mockUrlRequest2 = mock(UrlRequest.class);
|
||||||
@ -241,13 +243,13 @@ public final class CronetDataSourceTest {
|
|||||||
doAnswer(
|
doAnswer(
|
||||||
invocation -> {
|
invocation -> {
|
||||||
// Invoke the callback for the previous request.
|
// Invoke the callback for the previous request.
|
||||||
dataSourceUnderTest.urlRequestCallback.onFailed(
|
previousRequestCallback.onFailed(
|
||||||
mockUrlRequest,
|
mockUrlRequest,
|
||||||
testUrlResponseInfo,
|
testUrlResponseInfo,
|
||||||
createNetworkException(
|
createNetworkException(
|
||||||
/* errorCode= */ Integer.MAX_VALUE,
|
/* errorCode= */ Integer.MAX_VALUE,
|
||||||
/* cause= */ new IllegalArgumentException()));
|
/* cause= */ new IllegalArgumentException()));
|
||||||
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
dataSourceUnderTest.currentUrlRequestCallback.onResponseStarted(
|
||||||
mockUrlRequest2, testUrlResponseInfo);
|
mockUrlRequest2, testUrlResponseInfo);
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
@ -1094,7 +1096,8 @@ public final class CronetDataSourceTest {
|
|||||||
/* nowMs= */ startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
/* nowMs= */ startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
||||||
assertNotCountedDown(openLatch);
|
assertNotCountedDown(openLatch);
|
||||||
// The response arrives just in time.
|
// The response arrives just in time.
|
||||||
dataSourceUnderTest.urlRequestCallback.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
dataSourceUnderTest.currentUrlRequestCallback.onResponseStarted(
|
||||||
|
mockUrlRequest, testUrlResponseInfo);
|
||||||
openLatch.await();
|
openLatch.await();
|
||||||
assertThat(exceptionOnTestThread.get()).isNull();
|
assertThat(exceptionOnTestThread.get()).isNull();
|
||||||
}
|
}
|
||||||
@ -1130,7 +1133,7 @@ public final class CronetDataSourceTest {
|
|||||||
/* nowMs= */ startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
/* nowMs= */ startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
||||||
assertNotCountedDown(timedOutLatch);
|
assertNotCountedDown(timedOutLatch);
|
||||||
// A redirect arrives just in time.
|
// A redirect arrives just in time.
|
||||||
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
dataSourceUnderTest.currentUrlRequestCallback.onRedirectReceived(
|
||||||
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl1");
|
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl1");
|
||||||
|
|
||||||
long newTimeoutMs = 2 * TEST_CONNECT_TIMEOUT_MS - 1;
|
long newTimeoutMs = 2 * TEST_CONNECT_TIMEOUT_MS - 1;
|
||||||
@ -1138,7 +1141,7 @@ public final class CronetDataSourceTest {
|
|||||||
// We should still be trying to open as we approach the new timeout.
|
// We should still be trying to open as we approach the new timeout.
|
||||||
assertNotCountedDown(timedOutLatch);
|
assertNotCountedDown(timedOutLatch);
|
||||||
// A redirect arrives just in time.
|
// A redirect arrives just in time.
|
||||||
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
dataSourceUnderTest.currentUrlRequestCallback.onRedirectReceived(
|
||||||
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl2");
|
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl2");
|
||||||
|
|
||||||
newTimeoutMs = 3 * TEST_CONNECT_TIMEOUT_MS - 2;
|
newTimeoutMs = 3 * TEST_CONNECT_TIMEOUT_MS - 2;
|
||||||
@ -1223,6 +1226,30 @@ public final class CronetDataSourceTest {
|
|||||||
verify(mockUrlRequest, times(2)).start();
|
verify(mockUrlRequest, times(2)).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRedirect_dataSourceHandlesSetCookie_previousRequestIsCanceled()
|
||||||
|
throws HttpDataSourceException {
|
||||||
|
dataSourceUnderTest =
|
||||||
|
(CronetDataSource)
|
||||||
|
new CronetDataSource.Factory(mockCronetEngine, executorService)
|
||||||
|
.setConnectionTimeoutMs(TEST_CONNECT_TIMEOUT_MS)
|
||||||
|
.setReadTimeoutMs(TEST_READ_TIMEOUT_MS)
|
||||||
|
.setResetTimeoutOnRedirects(true)
|
||||||
|
.setHandleSetCookieRequests(true)
|
||||||
|
.createDataSource();
|
||||||
|
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
|
||||||
|
|
||||||
|
mockSingleRedirectSuccess(/* responseCode= */ 300);
|
||||||
|
|
||||||
|
testResponseHeader.put("Set-Cookie", "testcookie=testcookie; Path=/video");
|
||||||
|
|
||||||
|
dataSourceUnderTest.open(testDataSpec);
|
||||||
|
|
||||||
|
verify(mockUrlRequest, never()).followRedirect();
|
||||||
|
verify(mockUrlRequest).cancel();
|
||||||
|
verify(mockUrlRequest, times(2)).start();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void redirectNoSetCookieFollowsRedirect() throws HttpDataSourceException {
|
public void redirectNoSetCookieFollowsRedirect() throws HttpDataSourceException {
|
||||||
mockSingleRedirectSuccess(/* responseCode= */ 300);
|
mockSingleRedirectSuccess(/* responseCode= */ 300);
|
||||||
@ -1560,7 +1587,7 @@ public final class CronetDataSourceTest {
|
|||||||
private void mockResponseStartSuccess() {
|
private void mockResponseStartSuccess() {
|
||||||
doAnswer(
|
doAnswer(
|
||||||
invocation -> {
|
invocation -> {
|
||||||
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
dataSourceUnderTest.currentUrlRequestCallback.onResponseStarted(
|
||||||
mockUrlRequest, testUrlResponseInfo);
|
mockUrlRequest, testUrlResponseInfo);
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
@ -1571,7 +1598,7 @@ public final class CronetDataSourceTest {
|
|||||||
private void mockResponseStartRedirect() {
|
private void mockResponseStartRedirect() {
|
||||||
doAnswer(
|
doAnswer(
|
||||||
invocation -> {
|
invocation -> {
|
||||||
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
dataSourceUnderTest.currentUrlRequestCallback.onRedirectReceived(
|
||||||
mockUrlRequest,
|
mockUrlRequest,
|
||||||
createUrlResponseInfo(307), // statusCode
|
createUrlResponseInfo(307), // statusCode
|
||||||
"http://redirect.location.com");
|
"http://redirect.location.com");
|
||||||
@ -1586,12 +1613,12 @@ public final class CronetDataSourceTest {
|
|||||||
invocation -> {
|
invocation -> {
|
||||||
if (!redirectCalled) {
|
if (!redirectCalled) {
|
||||||
redirectCalled = true;
|
redirectCalled = true;
|
||||||
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
dataSourceUnderTest.currentUrlRequestCallback.onRedirectReceived(
|
||||||
mockUrlRequest,
|
mockUrlRequest,
|
||||||
createUrlResponseInfoWithUrl("http://example.com/video", responseCode),
|
createUrlResponseInfoWithUrl("http://example.com/video", responseCode),
|
||||||
"http://example.com/video/redirect");
|
"http://example.com/video/redirect");
|
||||||
} else {
|
} else {
|
||||||
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
dataSourceUnderTest.currentUrlRequestCallback.onResponseStarted(
|
||||||
mockUrlRequest, testUrlResponseInfo);
|
mockUrlRequest, testUrlResponseInfo);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -1603,7 +1630,7 @@ public final class CronetDataSourceTest {
|
|||||||
private void mockFollowRedirectSuccess() {
|
private void mockFollowRedirectSuccess() {
|
||||||
doAnswer(
|
doAnswer(
|
||||||
invocation -> {
|
invocation -> {
|
||||||
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
dataSourceUnderTest.currentUrlRequestCallback.onResponseStarted(
|
||||||
mockUrlRequest, testUrlResponseInfo);
|
mockUrlRequest, testUrlResponseInfo);
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
@ -1614,7 +1641,7 @@ public final class CronetDataSourceTest {
|
|||||||
private void mockResponseStartFailure(int errorCode, Throwable cause) {
|
private void mockResponseStartFailure(int errorCode, Throwable cause) {
|
||||||
doAnswer(
|
doAnswer(
|
||||||
invocation -> {
|
invocation -> {
|
||||||
dataSourceUnderTest.urlRequestCallback.onFailed(
|
dataSourceUnderTest.currentUrlRequestCallback.onFailed(
|
||||||
mockUrlRequest,
|
mockUrlRequest,
|
||||||
createUrlResponseInfo(500), // statusCode
|
createUrlResponseInfo(500), // statusCode
|
||||||
createNetworkException(errorCode, cause));
|
createNetworkException(errorCode, cause));
|
||||||
@ -1629,7 +1656,7 @@ public final class CronetDataSourceTest {
|
|||||||
doAnswer(
|
doAnswer(
|
||||||
invocation -> {
|
invocation -> {
|
||||||
if (positionAndRemaining[1] == 0) {
|
if (positionAndRemaining[1] == 0) {
|
||||||
dataSourceUnderTest.urlRequestCallback.onSucceeded(
|
dataSourceUnderTest.currentUrlRequestCallback.onSucceeded(
|
||||||
mockUrlRequest, testUrlResponseInfo);
|
mockUrlRequest, testUrlResponseInfo);
|
||||||
} else {
|
} else {
|
||||||
ByteBuffer inputBuffer = (ByteBuffer) invocation.getArguments()[0];
|
ByteBuffer inputBuffer = (ByteBuffer) invocation.getArguments()[0];
|
||||||
@ -1637,7 +1664,7 @@ public final class CronetDataSourceTest {
|
|||||||
inputBuffer.put(buildTestDataBuffer(positionAndRemaining[0], readLength));
|
inputBuffer.put(buildTestDataBuffer(positionAndRemaining[0], readLength));
|
||||||
positionAndRemaining[0] += readLength;
|
positionAndRemaining[0] += readLength;
|
||||||
positionAndRemaining[1] -= readLength;
|
positionAndRemaining[1] -= readLength;
|
||||||
dataSourceUnderTest.urlRequestCallback.onReadCompleted(
|
dataSourceUnderTest.currentUrlRequestCallback.onReadCompleted(
|
||||||
mockUrlRequest, testUrlResponseInfo, inputBuffer);
|
mockUrlRequest, testUrlResponseInfo, inputBuffer);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -1649,7 +1676,7 @@ public final class CronetDataSourceTest {
|
|||||||
private void mockReadFailure() {
|
private void mockReadFailure() {
|
||||||
doAnswer(
|
doAnswer(
|
||||||
invocation -> {
|
invocation -> {
|
||||||
dataSourceUnderTest.urlRequestCallback.onFailed(
|
dataSourceUnderTest.currentUrlRequestCallback.onFailed(
|
||||||
mockUrlRequest,
|
mockUrlRequest,
|
||||||
createUrlResponseInfo(500), // statusCode
|
createUrlResponseInfo(500), // statusCode
|
||||||
createNetworkException(
|
createNetworkException(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user