diff --git a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java index e4949d3630..422f4d5089 100644 --- a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java +++ b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java @@ -34,13 +34,11 @@ import android.net.http.HttpEngine; import android.net.http.HttpException; import android.net.http.InlineExecutionProhibitedException; import android.net.http.UploadDataProvider; -import android.net.http.UploadDataSink; import android.net.http.UrlRequest; import android.net.http.UrlRequest.Status; import android.net.http.UrlResponseInfo; import android.net.http.cts.util.HttpCtsTestServer; 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.ResponseStep; import android.net.http.cts.util.UploadDataProviders; @@ -140,14 +138,11 @@ public class UrlRequestTest { } @Test - public void testUrlRequestPost_EchoRequestBody() throws Exception { + public void testUrlRequestPost_EchoRequestBody() { String testData = "test"; UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getEchoBodyUrl()); - TestUploadDataProvider dataProvider = - new TestUploadDataProvider( - TestUploadDataProvider.SuccessCallbackMode.SYNC, mCallback.getExecutor()); - dataProvider.addRead(testData.getBytes()); + UploadDataProvider dataProvider = UploadDataProviders.create(testData); builder.setUploadDataProvider(dataProvider, mCallback.getExecutor()); builder.addHeader("Content-Type", "text/html"); builder.build().start(); @@ -155,7 +150,6 @@ public class UrlRequestTest { assertOKStatusCode(mCallback.mResponseInfo); assertEquals(testData, mCallback.mResponseAsString); - dataProvider.assertClosed(); } @Test @@ -170,7 +164,7 @@ public class UrlRequestTest { callback.setAllowDirectExecutor(true); UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder( mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback); - UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test"); + UploadDataProvider dataProvider = UploadDataProviders.create("test"); builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR); builder.addHeader("Content-Type", "text/plain;charset=UTF-8"); builder.setDirectExecutorAllowed(true); @@ -193,7 +187,7 @@ public class UrlRequestTest { UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder( mTestServer.getEchoBodyUrl(), Executors.newSingleThreadExecutor(), callback); - UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test"); + UploadDataProvider dataProvider = UploadDataProviders.create("test"); builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR) .addHeader("Content-Type", "text/plain;charset=UTF-8") @@ -213,7 +207,7 @@ public class UrlRequestTest { UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder( mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback); - UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test"); + UploadDataProvider dataProvider = UploadDataProviders.create("test"); builder.setUploadDataProvider(dataProvider, Executors.newSingleThreadExecutor()) .addHeader("Content-Type", "text/plain;charset=UTF-8") @@ -415,39 +409,4 @@ public class UrlRequestTest { 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; - } - } } diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java b/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java deleted file mode 100644 index d04782896d..0000000000 --- a/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java +++ /dev/null @@ -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 mReads = new ArrayList(); - 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"); - } - } -} diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java b/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java index 889f8f2f5e..3b90fa0f02 100644 --- a/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java +++ b/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java @@ -25,6 +25,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; /** * 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); } + /** + * 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 { FileChannel getChannel() throws IOException; }