Merge "Migrate CTS test users of UploadDataProvider to a single canonical impl."
This commit is contained in:
@@ -34,13 +34,11 @@ import android.net.http.HttpEngine;
|
|||||||
import android.net.http.HttpException;
|
import android.net.http.HttpException;
|
||||||
import android.net.http.InlineExecutionProhibitedException;
|
import android.net.http.InlineExecutionProhibitedException;
|
||||||
import android.net.http.UploadDataProvider;
|
import android.net.http.UploadDataProvider;
|
||||||
import android.net.http.UploadDataSink;
|
|
||||||
import android.net.http.UrlRequest;
|
import android.net.http.UrlRequest;
|
||||||
import android.net.http.UrlRequest.Status;
|
import android.net.http.UrlRequest.Status;
|
||||||
import android.net.http.UrlResponseInfo;
|
import android.net.http.UrlResponseInfo;
|
||||||
import android.net.http.cts.util.HttpCtsTestServer;
|
import android.net.http.cts.util.HttpCtsTestServer;
|
||||||
import android.net.http.cts.util.TestStatusListener;
|
import android.net.http.cts.util.TestStatusListener;
|
||||||
import android.net.http.cts.util.TestUploadDataProvider;
|
|
||||||
import android.net.http.cts.util.TestUrlRequestCallback;
|
import android.net.http.cts.util.TestUrlRequestCallback;
|
||||||
import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
|
import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
|
||||||
import android.net.http.cts.util.UploadDataProviders;
|
import android.net.http.cts.util.UploadDataProviders;
|
||||||
@@ -140,14 +138,11 @@ public class UrlRequestTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUrlRequestPost_EchoRequestBody() throws Exception {
|
public void testUrlRequestPost_EchoRequestBody() {
|
||||||
String testData = "test";
|
String testData = "test";
|
||||||
UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getEchoBodyUrl());
|
UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getEchoBodyUrl());
|
||||||
|
|
||||||
TestUploadDataProvider dataProvider =
|
UploadDataProvider dataProvider = UploadDataProviders.create(testData);
|
||||||
new TestUploadDataProvider(
|
|
||||||
TestUploadDataProvider.SuccessCallbackMode.SYNC, mCallback.getExecutor());
|
|
||||||
dataProvider.addRead(testData.getBytes());
|
|
||||||
builder.setUploadDataProvider(dataProvider, mCallback.getExecutor());
|
builder.setUploadDataProvider(dataProvider, mCallback.getExecutor());
|
||||||
builder.addHeader("Content-Type", "text/html");
|
builder.addHeader("Content-Type", "text/html");
|
||||||
builder.build().start();
|
builder.build().start();
|
||||||
@@ -155,7 +150,6 @@ public class UrlRequestTest {
|
|||||||
|
|
||||||
assertOKStatusCode(mCallback.mResponseInfo);
|
assertOKStatusCode(mCallback.mResponseInfo);
|
||||||
assertEquals(testData, mCallback.mResponseAsString);
|
assertEquals(testData, mCallback.mResponseAsString);
|
||||||
dataProvider.assertClosed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -170,7 +164,7 @@ public class UrlRequestTest {
|
|||||||
callback.setAllowDirectExecutor(true);
|
callback.setAllowDirectExecutor(true);
|
||||||
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
|
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
|
||||||
mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback);
|
mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback);
|
||||||
UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test");
|
UploadDataProvider dataProvider = UploadDataProviders.create("test");
|
||||||
builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR);
|
builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR);
|
||||||
builder.addHeader("Content-Type", "text/plain;charset=UTF-8");
|
builder.addHeader("Content-Type", "text/plain;charset=UTF-8");
|
||||||
builder.setDirectExecutorAllowed(true);
|
builder.setDirectExecutorAllowed(true);
|
||||||
@@ -193,7 +187,7 @@ public class UrlRequestTest {
|
|||||||
|
|
||||||
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
|
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
|
||||||
mTestServer.getEchoBodyUrl(), Executors.newSingleThreadExecutor(), callback);
|
mTestServer.getEchoBodyUrl(), Executors.newSingleThreadExecutor(), callback);
|
||||||
UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test");
|
UploadDataProvider dataProvider = UploadDataProviders.create("test");
|
||||||
|
|
||||||
builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR)
|
builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR)
|
||||||
.addHeader("Content-Type", "text/plain;charset=UTF-8")
|
.addHeader("Content-Type", "text/plain;charset=UTF-8")
|
||||||
@@ -213,7 +207,7 @@ public class UrlRequestTest {
|
|||||||
|
|
||||||
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
|
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
|
||||||
mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback);
|
mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback);
|
||||||
UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test");
|
UploadDataProvider dataProvider = UploadDataProviders.create("test");
|
||||||
|
|
||||||
builder.setUploadDataProvider(dataProvider, Executors.newSingleThreadExecutor())
|
builder.setUploadDataProvider(dataProvider, Executors.newSingleThreadExecutor())
|
||||||
.addHeader("Content-Type", "text/plain;charset=UTF-8")
|
.addHeader("Content-Type", "text/plain;charset=UTF-8")
|
||||||
@@ -415,39 +409,4 @@ public class UrlRequestTest {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class InMemoryUploadDataProvider extends UploadDataProvider {
|
|
||||||
private final byte[] mBody;
|
|
||||||
private int mNextChunkStartIndex = 0;
|
|
||||||
|
|
||||||
private InMemoryUploadDataProvider(byte[] body) {
|
|
||||||
this.mBody = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
static InMemoryUploadDataProvider fromUtf8String(String body) {
|
|
||||||
return new InMemoryUploadDataProvider(body.getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getLength() {
|
|
||||||
return mBody.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) {
|
|
||||||
if (mNextChunkStartIndex >= getLength()) {
|
|
||||||
throw new IllegalStateException("Body of known length is exhausted");
|
|
||||||
}
|
|
||||||
int nextChunkSize =
|
|
||||||
Math.min(mBody.length - mNextChunkStartIndex, byteBuffer.remaining());
|
|
||||||
byteBuffer.put(mBody, mNextChunkStartIndex, nextChunkSize);
|
|
||||||
mNextChunkStartIndex += nextChunkSize;
|
|
||||||
uploadDataSink.onReadSucceeded(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void rewind(UploadDataSink uploadDataSink) {
|
|
||||||
mNextChunkStartIndex = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,294 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package android.net.http.cts.util;
|
|
||||||
|
|
||||||
import android.net.http.UploadDataProvider;
|
|
||||||
import android.net.http.UploadDataSink;
|
|
||||||
import android.os.ConditionVariable;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
/** An UploadDataProvider implementation used in tests. */
|
|
||||||
public class TestUploadDataProvider extends UploadDataProvider {
|
|
||||||
// Indicates whether all success callbacks are synchronous or asynchronous.
|
|
||||||
// Doesn't apply to errors.
|
|
||||||
public enum SuccessCallbackMode {
|
|
||||||
SYNC,
|
|
||||||
ASYNC
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indicates whether failures should throw exceptions, invoke callbacks
|
|
||||||
// synchronously, or invoke callback asynchronously.
|
|
||||||
public enum FailMode {
|
|
||||||
NONE,
|
|
||||||
THROWN,
|
|
||||||
CALLBACK_SYNC,
|
|
||||||
CALLBACK_ASYNC
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ArrayList<byte[]> mReads = new ArrayList<byte[]>();
|
|
||||||
private final SuccessCallbackMode mSuccessCallbackMode;
|
|
||||||
private final Executor mExecutor;
|
|
||||||
|
|
||||||
private boolean mChunked;
|
|
||||||
|
|
||||||
// Index of read to fail on.
|
|
||||||
private int mReadFailIndex = -1;
|
|
||||||
// Indicates how to fail on a read.
|
|
||||||
private FailMode mReadFailMode = FailMode.NONE;
|
|
||||||
|
|
||||||
private FailMode mRewindFailMode = FailMode.NONE;
|
|
||||||
|
|
||||||
private FailMode mLengthFailMode = FailMode.NONE;
|
|
||||||
|
|
||||||
private int mNumReadCalls;
|
|
||||||
private int mNumRewindCalls;
|
|
||||||
|
|
||||||
private int mNextRead;
|
|
||||||
private boolean mStarted;
|
|
||||||
private boolean mReadPending;
|
|
||||||
private boolean mRewindPending;
|
|
||||||
// Used to ensure there are no read/rewind requests after a failure.
|
|
||||||
private boolean mFailed;
|
|
||||||
|
|
||||||
private final AtomicBoolean mClosed = new AtomicBoolean(false);
|
|
||||||
private final ConditionVariable mAwaitingClose = new ConditionVariable(false);
|
|
||||||
|
|
||||||
public TestUploadDataProvider(
|
|
||||||
SuccessCallbackMode successCallbackMode, final Executor executor) {
|
|
||||||
mSuccessCallbackMode = successCallbackMode;
|
|
||||||
mExecutor = executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds the result to be returned by a successful read request. The
|
|
||||||
// returned bytes must all fit within the read buffer provided by Cronet.
|
|
||||||
// After a rewind, if there is one, all reads will be repeated.
|
|
||||||
public void addRead(byte[] read) {
|
|
||||||
if (mStarted) {
|
|
||||||
throw new IllegalStateException("Adding bytes after read");
|
|
||||||
}
|
|
||||||
mReads.add(read);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReadFailure(int readFailIndex, FailMode readFailMode) {
|
|
||||||
mReadFailIndex = readFailIndex;
|
|
||||||
mReadFailMode = readFailMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLengthFailure() {
|
|
||||||
mLengthFailMode = FailMode.THROWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRewindFailure(FailMode rewindFailMode) {
|
|
||||||
mRewindFailMode = rewindFailMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChunked(boolean chunked) {
|
|
||||||
mChunked = chunked;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumReadCalls() {
|
|
||||||
return mNumReadCalls;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumRewindCalls() {
|
|
||||||
return mNumRewindCalls;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the cumulative length of all data added by calls to addRead. */
|
|
||||||
@Override
|
|
||||||
public long getLength() throws IOException {
|
|
||||||
if (mClosed.get()) {
|
|
||||||
throw new ClosedChannelException();
|
|
||||||
}
|
|
||||||
if (mLengthFailMode == FailMode.THROWN) {
|
|
||||||
throw new IllegalStateException("Sync length failure");
|
|
||||||
}
|
|
||||||
return getUploadedLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getUploadedLength() {
|
|
||||||
if (mChunked) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
long length = 0;
|
|
||||||
for (byte[] read : mReads) {
|
|
||||||
length += read.length;
|
|
||||||
}
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void read(final UploadDataSink uploadDataSink, final ByteBuffer byteBuffer)
|
|
||||||
throws IOException {
|
|
||||||
int currentReadCall = mNumReadCalls;
|
|
||||||
++mNumReadCalls;
|
|
||||||
if (mClosed.get()) {
|
|
||||||
throw new ClosedChannelException();
|
|
||||||
}
|
|
||||||
assertIdle();
|
|
||||||
|
|
||||||
if (maybeFailRead(currentReadCall, uploadDataSink)) {
|
|
||||||
mFailed = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mReadPending = true;
|
|
||||||
mStarted = true;
|
|
||||||
|
|
||||||
final boolean finalChunk = (mChunked && mNextRead == mReads.size() - 1);
|
|
||||||
if (mNextRead < mReads.size()) {
|
|
||||||
if ((byteBuffer.limit() - byteBuffer.position()) < mReads.get(mNextRead).length) {
|
|
||||||
throw new IllegalStateException("Read buffer smaller than expected.");
|
|
||||||
}
|
|
||||||
byteBuffer.put(mReads.get(mNextRead));
|
|
||||||
++mNextRead;
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Too many reads: " + mNextRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
Runnable completeRunnable =
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
mReadPending = false;
|
|
||||||
uploadDataSink.onReadSucceeded(finalChunk);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (mSuccessCallbackMode == SuccessCallbackMode.SYNC) {
|
|
||||||
completeRunnable.run();
|
|
||||||
} else {
|
|
||||||
mExecutor.execute(completeRunnable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void rewind(final UploadDataSink uploadDataSink) throws IOException {
|
|
||||||
++mNumRewindCalls;
|
|
||||||
if (mClosed.get()) {
|
|
||||||
throw new ClosedChannelException();
|
|
||||||
}
|
|
||||||
assertIdle();
|
|
||||||
|
|
||||||
if (maybeFailRewind(uploadDataSink)) {
|
|
||||||
mFailed = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mNextRead == 0) {
|
|
||||||
// Should never try and rewind when rewinding does nothing.
|
|
||||||
throw new IllegalStateException("Unexpected rewind when already at beginning");
|
|
||||||
}
|
|
||||||
|
|
||||||
mRewindPending = true;
|
|
||||||
mNextRead = 0;
|
|
||||||
|
|
||||||
Runnable completeRunnable =
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
mRewindPending = false;
|
|
||||||
uploadDataSink.onRewindSucceeded();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (mSuccessCallbackMode == SuccessCallbackMode.SYNC) {
|
|
||||||
completeRunnable.run();
|
|
||||||
} else {
|
|
||||||
mExecutor.execute(completeRunnable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertIdle() {
|
|
||||||
if (mReadPending) {
|
|
||||||
throw new IllegalStateException("Unexpected operation during read");
|
|
||||||
}
|
|
||||||
if (mRewindPending) {
|
|
||||||
throw new IllegalStateException("Unexpected operation during rewind");
|
|
||||||
}
|
|
||||||
if (mFailed) {
|
|
||||||
throw new IllegalStateException("Unexpected operation after failure");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean maybeFailRead(int readIndex, final UploadDataSink uploadDataSink) {
|
|
||||||
if (readIndex != mReadFailIndex) return false;
|
|
||||||
|
|
||||||
switch (mReadFailMode) {
|
|
||||||
case THROWN:
|
|
||||||
throw new IllegalStateException("Thrown read failure");
|
|
||||||
case CALLBACK_SYNC:
|
|
||||||
uploadDataSink.onReadError(new IllegalStateException("Sync read failure"));
|
|
||||||
return true;
|
|
||||||
case CALLBACK_ASYNC:
|
|
||||||
Runnable errorRunnable =
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
uploadDataSink.onReadError(
|
|
||||||
new IllegalStateException("Async read failure"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mExecutor.execute(errorRunnable);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean maybeFailRewind(final UploadDataSink uploadDataSink) {
|
|
||||||
switch (mRewindFailMode) {
|
|
||||||
case THROWN:
|
|
||||||
throw new IllegalStateException("Thrown rewind failure");
|
|
||||||
case CALLBACK_SYNC:
|
|
||||||
uploadDataSink.onRewindError(new IllegalStateException("Sync rewind failure"));
|
|
||||||
return true;
|
|
||||||
case CALLBACK_ASYNC:
|
|
||||||
Runnable errorRunnable =
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
uploadDataSink.onRewindError(
|
|
||||||
new IllegalStateException("Async rewind failure"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mExecutor.execute(errorRunnable);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
if (!mClosed.compareAndSet(false, true)) {
|
|
||||||
throw new AssertionError("Closed twice");
|
|
||||||
}
|
|
||||||
mAwaitingClose.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assertClosed() {
|
|
||||||
mAwaitingClose.block(5000);
|
|
||||||
if (!mClosed.get()) {
|
|
||||||
throw new AssertionError("Was not closed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,6 +25,7 @@ import java.io.FileInputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides implementations of {@link UploadDataProvider} for common use cases. Corresponds to
|
* Provides implementations of {@link UploadDataProvider} for common use cases. Corresponds to
|
||||||
@@ -91,6 +92,16 @@ public final class UploadDataProviders {
|
|||||||
return create(data, 0, data.length);
|
return create(data, 0, data.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads the UTF-8 representation of {@code data}
|
||||||
|
*
|
||||||
|
* @param data String containing data to upload
|
||||||
|
* @return A new UploadDataProvider for the given data
|
||||||
|
*/
|
||||||
|
public static UploadDataProvider create(String data) {
|
||||||
|
return create(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
private interface FileChannelProvider {
|
private interface FileChannelProvider {
|
||||||
FileChannel getChannel() throws IOException;
|
FileChannel getChannel() throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user