| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.net.urlconnection; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.junit.Assert.assertThrows; |
| |
| import androidx.test.ext.junit.runners.AndroidJUnit4; |
| import androidx.test.filters.SmallTest; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import org.chromium.base.test.util.Batch; |
| import org.chromium.net.CronetEngine; |
| import org.chromium.net.CronetTestRule; |
| import org.chromium.net.CronetTestRule.CronetImplementation; |
| import org.chromium.net.CronetTestRule.IgnoreFor; |
| import org.chromium.net.NativeTestServer; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.HttpURLConnection; |
| import java.net.ProtocolException; |
| import java.net.URL; |
| |
| /** Tests the CronetBufferedOutputStream implementation. */ |
| @Batch(Batch.UNIT_TESTS) |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK}, |
| reason = "See crrev.com/c/4590329") |
| @RunWith(AndroidJUnit4.class) |
| public class CronetBufferedOutputStreamTest { |
| @Rule public final CronetTestRule mTestRule = CronetTestRule.withAutomaticEngineStartup(); |
| |
| private HttpURLConnection mConnection; |
| |
| private CronetEngine mCronetEngine; |
| |
| @Before |
| public void setUp() throws Exception { |
| mCronetEngine = mTestRule.getTestFramework().getEngine(); |
| assertThat( |
| NativeTestServer.startNativeTestServer( |
| mTestRule.getTestFramework().getContext())) |
| .isTrue(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| if (mConnection != null) { |
| mConnection.disconnect(); |
| } |
| NativeTestServer.shutdownNativeTestServer(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testGetOutputStreamAfterConnectionMade() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThrows(ProtocolException.class, mConnection::getOutputStream); |
| } |
| |
| @Test |
| @SmallTest |
| public void testWriteAfterConnect() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| OutputStream out = mConnection.getOutputStream(); |
| out.write(TestUtil.UPLOAD_DATA); |
| mConnection.connect(); |
| // Attempt to write some more. |
| IllegalStateException e = |
| assertThrows(IllegalStateException.class, () -> out.write(TestUtil.UPLOAD_DATA)); |
| |
| assertThat(e) |
| .hasMessageThat() |
| .isEqualTo( |
| "Use setFixedLengthStreamingMode() or " |
| + "setChunkedStreamingMode() for writing after connect"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testWriteAfterReadingResponse() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| OutputStream out = mConnection.getOutputStream(); |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThrows(IllegalStateException.class, () -> out.write(TestUtil.UPLOAD_DATA)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testPostWithContentLength() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| byte[] largeData = TestUtil.getLargeData(); |
| mConnection.setRequestProperty("Content-Length", Integer.toString(largeData.length)); |
| OutputStream out = mConnection.getOutputStream(); |
| int totalBytesWritten = 0; |
| // Number of bytes to write each time. It is doubled each time |
| // to make sure that the buffer grows. |
| int bytesToWrite = 683; |
| while (totalBytesWritten < largeData.length) { |
| if (bytesToWrite > largeData.length - totalBytesWritten) { |
| // Do not write out of bound. |
| bytesToWrite = largeData.length - totalBytesWritten; |
| } |
| out.write(largeData, totalBytesWritten, bytesToWrite); |
| totalBytesWritten += bytesToWrite; |
| bytesToWrite *= 2; |
| } |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThat(mConnection.getResponseMessage()).isEqualTo("OK"); |
| TestUtil.checkLargeData(TestUtil.getResponseAsString(mConnection)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testPostWithContentLengthOneMassiveWrite() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| byte[] largeData = TestUtil.getLargeData(); |
| mConnection.setRequestProperty("Content-Length", Integer.toString(largeData.length)); |
| OutputStream out = mConnection.getOutputStream(); |
| out.write(largeData); |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThat(mConnection.getResponseMessage()).isEqualTo("OK"); |
| TestUtil.checkLargeData(TestUtil.getResponseAsString(mConnection)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testPostWithContentLengthWriteOneByte() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| byte[] largeData = TestUtil.getLargeData(); |
| mConnection.setRequestProperty("Content-Length", Integer.toString(largeData.length)); |
| OutputStream out = mConnection.getOutputStream(); |
| for (int i = 0; i < largeData.length; i++) { |
| out.write(largeData[i]); |
| } |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThat(mConnection.getResponseMessage()).isEqualTo("OK"); |
| TestUtil.checkLargeData(TestUtil.getResponseAsString(mConnection)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testPostWithZeroContentLength() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| mConnection.setRequestProperty("Content-Length", "0"); |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThat(mConnection.getResponseMessage()).isEqualTo("OK"); |
| assertThat(TestUtil.getResponseAsString(mConnection)).isEmpty(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testPostZeroByteWithoutContentLength() throws Exception { |
| // Make sure both implementation sets the Content-Length header to 0. |
| URL url = new URL(NativeTestServer.getEchoHeaderURL("Content-Length")); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThat(mConnection.getResponseMessage()).isEqualTo("OK"); |
| assertThat(TestUtil.getResponseAsString(mConnection)).isEqualTo("0"); |
| mConnection.disconnect(); |
| |
| // Make sure the server echoes back empty body for both implementation. |
| URL echoBody = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) echoBody.openConnection(); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThat(mConnection.getResponseMessage()).isEqualTo("OK"); |
| assertThat(TestUtil.getResponseAsString(mConnection)).isEmpty(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testPostWithoutContentLengthSmall() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| OutputStream out = mConnection.getOutputStream(); |
| out.write(TestUtil.UPLOAD_DATA); |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThat(mConnection.getResponseMessage()).isEqualTo("OK"); |
| assertThat(TestUtil.getResponseAsString(mConnection)) |
| .isEqualTo(TestUtil.UPLOAD_DATA_STRING); |
| } |
| |
| @Test |
| @SmallTest |
| public void testPostWithoutContentLength() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| byte[] largeData = TestUtil.getLargeData(); |
| OutputStream out = mConnection.getOutputStream(); |
| int totalBytesWritten = 0; |
| // Number of bytes to write each time. It is doubled each time |
| // to make sure that the buffer grows. |
| int bytesToWrite = 683; |
| while (totalBytesWritten < largeData.length) { |
| if (bytesToWrite > largeData.length - totalBytesWritten) { |
| // Do not write out of bound. |
| bytesToWrite = largeData.length - totalBytesWritten; |
| } |
| out.write(largeData, totalBytesWritten, bytesToWrite); |
| totalBytesWritten += bytesToWrite; |
| bytesToWrite *= 2; |
| } |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThat(mConnection.getResponseMessage()).isEqualTo("OK"); |
| TestUtil.checkLargeData(TestUtil.getResponseAsString(mConnection)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testPostWithoutContentLengthOneMassiveWrite() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| OutputStream out = mConnection.getOutputStream(); |
| byte[] largeData = TestUtil.getLargeData(); |
| out.write(largeData); |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThat(mConnection.getResponseMessage()).isEqualTo("OK"); |
| TestUtil.checkLargeData(TestUtil.getResponseAsString(mConnection)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testPostWithoutContentLengthWriteOneByte() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| OutputStream out = mConnection.getOutputStream(); |
| byte[] largeData = TestUtil.getLargeData(); |
| for (int i = 0; i < largeData.length; i++) { |
| out.write(largeData[i]); |
| } |
| assertThat(mConnection.getResponseCode()).isEqualTo(200); |
| assertThat(mConnection.getResponseMessage()).isEqualTo("OK"); |
| TestUtil.checkLargeData(TestUtil.getResponseAsString(mConnection)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testWriteLessThanContentLength() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| // Set a content length that's 1 byte more. |
| mConnection.setRequestProperty( |
| "Content-Length", Integer.toString(TestUtil.UPLOAD_DATA.length + 1)); |
| OutputStream out = mConnection.getOutputStream(); |
| out.write(TestUtil.UPLOAD_DATA); |
| assertThrows(IOException.class, mConnection::getResponseCode); |
| } |
| |
| /** Tests that if caller writes more than the content length provided, an exception should occur. */ |
| @Test |
| @SmallTest |
| public void testWriteMoreThanContentLength() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| // Use a content length that is 1 byte shorter than actual data. |
| mConnection.setRequestProperty( |
| "Content-Length", Integer.toString(TestUtil.UPLOAD_DATA.length - 1)); |
| OutputStream out = mConnection.getOutputStream(); |
| // Write a few bytes first. |
| out.write(TestUtil.UPLOAD_DATA, 0, 3); |
| // Write remaining bytes. |
| ProtocolException e = |
| assertThrows( |
| ProtocolException.class, |
| () -> out.write(TestUtil.UPLOAD_DATA, 3, TestUtil.UPLOAD_DATA.length - 3)); |
| assertThat(e) |
| .hasMessageThat() |
| .isEqualTo( |
| "exceeded content-length limit of " |
| + (TestUtil.UPLOAD_DATA.length - 1) |
| + " bytes"); |
| } |
| |
| /** Same as {@code testWriteMoreThanContentLength()}, but it only writes one byte at a time. */ |
| @Test |
| @SmallTest |
| public void testWriteMoreThanContentLengthWriteOneByte() throws Exception { |
| URL url = new URL(NativeTestServer.getEchoBodyURL()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| // Use a content length that is 1 byte shorter than actual data. |
| mConnection.setRequestProperty( |
| "Content-Length", Integer.toString(TestUtil.UPLOAD_DATA.length - 1)); |
| OutputStream out = mConnection.getOutputStream(); |
| ProtocolException e = |
| assertThrows( |
| ProtocolException.class, |
| () -> { |
| for (int i = 0; i < TestUtil.UPLOAD_DATA.length; i++) { |
| out.write(TestUtil.UPLOAD_DATA[i]); |
| } |
| }); |
| assertThat(e) |
| .hasMessageThat() |
| .isEqualTo( |
| "exceeded content-length limit of " |
| + (TestUtil.UPLOAD_DATA.length - 1) |
| + " bytes"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testRewind() throws Exception { |
| URL url = new URL(NativeTestServer.getRedirectToEchoBody()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| mConnection.setRequestProperty( |
| "Content-Length", Integer.toString(TestUtil.UPLOAD_DATA.length)); |
| OutputStream out = mConnection.getOutputStream(); |
| out.write(TestUtil.UPLOAD_DATA); |
| assertThat(TestUtil.getResponseAsString(mConnection)) |
| .isEqualTo(TestUtil.UPLOAD_DATA_STRING); |
| } |
| |
| /** Like {@link #testRewind} but does not set Content-Length header. */ |
| @Test |
| @SmallTest |
| public void testRewindWithoutContentLength() throws Exception { |
| URL url = new URL(NativeTestServer.getRedirectToEchoBody()); |
| mConnection = (HttpURLConnection) mCronetEngine.openConnection(url); |
| mConnection.setDoOutput(true); |
| mConnection.setRequestMethod("POST"); |
| OutputStream out = mConnection.getOutputStream(); |
| out.write(TestUtil.UPLOAD_DATA); |
| assertThat(TestUtil.getResponseAsString(mConnection)) |
| .isEqualTo(TestUtil.UPLOAD_DATA_STRING); |
| } |
| } |