 Chromium Code Reviews
 Chromium Code Reviews Issue 966743003:
  [Cronet] Implement getOutputStream in CronetHttpURLConnection  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@chunked_support
    
  
    Issue 966743003:
  [Cronet] Implement getOutputStream in CronetHttpURLConnection  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@chunked_support| Index: components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetOutputStreamTest.java | 
| diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetOutputStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetOutputStreamTest.java | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..b54f0356d49ce1ee431819dcde18cd399d328b1d | 
| --- /dev/null | 
| +++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetOutputStreamTest.java | 
| @@ -0,0 +1,267 @@ | 
| +// Copyright 2015 The Chromium Authors. All rights reserved. | 
| +// 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 android.test.suitebuilder.annotation.SmallTest; | 
| + | 
| +import org.chromium.base.test.util.Feature; | 
| +import org.chromium.net.CronetTestActivity; | 
| +import org.chromium.net.CronetTestBase; | 
| +import org.chromium.net.NativeTestServer; | 
| + | 
| +import java.io.ByteArrayOutputStream; | 
| +import java.io.InputStream; | 
| +import java.io.OutputStream; | 
| +import java.net.HttpURLConnection; | 
| +import java.net.ProtocolException; | 
| +import java.net.URL; | 
| + | 
| +/** | 
| + * Tests {@code getOutputStream} when {@code setFixedLengthStreamingMode} is | 
| + * enabled. | 
| + * Tests annotated with {@code CompareDefaultWithCronet} will run once with the | 
| + * default HttpURLConnection implementation and then with Cronet's | 
| + * HttpURLConnection implementation. Tests annotated with | 
| + * {@code OnlyRunCronetHttpURLConnection} only run Cronet's implementation. | 
| + * See {@link CronetTestBase#runTest()} for details. | 
| + */ | 
| +public class CronetOutputStreamTest extends CronetTestBase { | 
| + private static final String UPLOAD_DATA_STRING = "Nifty upload data!"; | 
| + private static final byte[] UPLOAD_DATA = UPLOAD_DATA_STRING.getBytes(); | 
| + private static final int REPEAT_COUNT = 100000; | 
| + private CronetTestActivity mActivity; | 
| + | 
| + @Override | 
| + protected void setUp() throws Exception { | 
| + super.setUp(); | 
| + mActivity = launchCronetTestApp(); | 
| + assertTrue(NativeTestServer.startNativeTestServer( | 
| + getInstrumentation().getTargetContext())); | 
| + } | 
| + | 
| + @Override | 
| + protected void tearDown() throws Exception { | 
| + NativeTestServer.shutdownNativeTestServer(); | 
| + super.tearDown(); | 
| + } | 
| + | 
| + @SmallTest | 
| + @Feature({"Cronet"}) | 
| + @CompareDefaultWithCronet | 
| + public void testFixedLengthStreamingModeZeroContentLength() | 
| + throws Exception { | 
| + // Check content length is set. | 
| + URL echoLength = new URL(NativeTestServer.getEchoHeaderURL("Content-Length")); | 
| + HttpURLConnection connection1 = | 
| + (HttpURLConnection) echoLength.openConnection(); | 
| + connection1.setDoOutput(true); | 
| + connection1.setRequestMethod("POST"); | 
| + connection1.setFixedLengthStreamingMode(0); | 
| + assertEquals(200, connection1.getResponseCode()); | 
| + assertEquals("OK", connection1.getResponseMessage()); | 
| + assertEquals("0", getResponseAsString(connection1)); | 
| + connection1.disconnect(); | 
| + | 
| + // Check body is empty. | 
| + URL echoBody = new URL(NativeTestServer.getEchoBodyURL()); | 
| + HttpURLConnection connection2 = | 
| + (HttpURLConnection) echoBody.openConnection(); | 
| + connection2.setDoOutput(true); | 
| + connection2.setRequestMethod("POST"); | 
| + connection2.setFixedLengthStreamingMode(0); | 
| + assertEquals(200, connection2.getResponseCode()); | 
| + assertEquals("OK", connection2.getResponseMessage()); | 
| + assertEquals("", getResponseAsString(connection2)); | 
| + connection2.disconnect(); | 
| + } | 
| + | 
| + @SmallTest | 
| + @Feature({"Cronet"}) | 
| + @CompareDefaultWithCronet | 
| + public void testWriteMoreThanContentLength() | 
| + throws Exception { | 
| + URL url = new URL(NativeTestServer.getEchoBodyURL()); | 
| + HttpURLConnection connection = | 
| + (HttpURLConnection) url.openConnection(); | 
| + connection.setDoOutput(true); | 
| + connection.setRequestMethod("POST"); | 
| + // Set a content length that's 1 byte short. | 
| + connection.setFixedLengthStreamingMode(UPLOAD_DATA.length - 1); | 
| + OutputStream out = connection.getOutputStream(); | 
| + try { | 
| + out.write(UPLOAD_DATA); | 
| + fail(); | 
| + } catch (ProtocolException e) { | 
| + // Expected. | 
| + assertEquals("expected " + (UPLOAD_DATA.length - 1) | 
| + + " bytes but received " | 
| + + UPLOAD_DATA.length, e.getMessage()); | 
| + } | 
| 
mmenke
2015/03/25 18:34:58
Should probably call disconnect at the end of all
 
xunjieli
2015/03/27 17:46:45
Done. Yes you are right. The underlying API makes
 | 
| + } | 
| + | 
| + @SmallTest | 
| + @Feature({"Cronet"}) | 
| + @CompareDefaultWithCronet | 
| + public void testWriteMoreThanContentLengthWriteOneByte() | 
| + throws Exception { | 
| + URL url = new URL(NativeTestServer.getEchoBodyURL()); | 
| + HttpURLConnection connection = | 
| + (HttpURLConnection) url.openConnection(); | 
| + connection.setDoOutput(true); | 
| + connection.setRequestMethod("POST"); | 
| + // Set a content length that's 1 byte short. | 
| + connection.setFixedLengthStreamingMode(UPLOAD_DATA.length - 1); | 
| + OutputStream out = connection.getOutputStream(); | 
| + try { | 
| + for (int i = 0; i < UPLOAD_DATA.length; i++) { | 
| + out.write(UPLOAD_DATA[i]); | 
| 
mmenke
2015/03/25 18:34:58
Suggest modifying one of these two tests so we act
 
xunjieli
2015/03/27 17:46:45
Done.
 | 
| + } | 
| + fail(); | 
| + } catch (ProtocolException e) { | 
| + // Expected. | 
| + assertEquals("expected 0 bytes but received 1", e.getMessage()); | 
| + } | 
| + } | 
| + | 
| + | 
| + @SmallTest | 
| + @Feature({"Cronet"}) | 
| + @CompareDefaultWithCronet | 
| + public void testFixedLengthStreamingMode() throws Exception { | 
| + URL url = new URL(NativeTestServer.getEchoBodyURL()); | 
| + HttpURLConnection connection = | 
| + (HttpURLConnection) url.openConnection(); | 
| + connection.setDoOutput(true); | 
| + connection.setRequestMethod("POST"); | 
| + connection.setFixedLengthStreamingMode(UPLOAD_DATA.length); | 
| + OutputStream out = connection.getOutputStream(); | 
| + out.write(UPLOAD_DATA); | 
| + assertEquals(200, connection.getResponseCode()); | 
| + assertEquals("OK", connection.getResponseMessage()); | 
| + assertEquals(UPLOAD_DATA_STRING, getResponseAsString(connection)); | 
| + connection.disconnect(); | 
| + } | 
| + | 
| + @SmallTest | 
| + @Feature({"Cronet"}) | 
| + @CompareDefaultWithCronet | 
| + public void testFixedLengthStreamingModeWriteOneByte() | 
| + throws Exception { | 
| + URL url = new URL(NativeTestServer.getEchoBodyURL()); | 
| + HttpURLConnection connection = | 
| + (HttpURLConnection) url.openConnection(); | 
| + connection.setDoOutput(true); | 
| + connection.setRequestMethod("POST"); | 
| + connection.setFixedLengthStreamingMode(UPLOAD_DATA.length); | 
| + OutputStream out = connection.getOutputStream(); | 
| + for (int i = 0; i < UPLOAD_DATA.length; i++) { | 
| + // Write one byte at a time. | 
| + out.write(UPLOAD_DATA[i]); | 
| + } | 
| + assertEquals(200, connection.getResponseCode()); | 
| + assertEquals("OK", connection.getResponseMessage()); | 
| + assertEquals(UPLOAD_DATA_STRING, getResponseAsString(connection)); | 
| + connection.disconnect(); | 
| + } | 
| + | 
| + @SmallTest | 
| + @Feature({"Cronet"}) | 
| + @CompareDefaultWithCronet | 
| + public void testFixedLengthStreamingModeLargeData() throws Exception { | 
| 
mmenke
2015/03/25 18:34:58
Maybe add a test like this where we just do one ma
 
xunjieli
2015/03/27 17:46:45
Done.
 | 
| + URL url = new URL(NativeTestServer.getEchoBodyURL()); | 
| + HttpURLConnection connection = | 
| + (HttpURLConnection) url.openConnection(); | 
| + connection.setDoOutput(true); | 
| + connection.setRequestMethod("POST"); | 
| + byte[] largeData = new byte[REPEAT_COUNT * UPLOAD_DATA.length]; | 
| + for (int i = 0; i < REPEAT_COUNT; i++) { | 
| + for (int j = 0; j < UPLOAD_DATA.length; j++) { | 
| + largeData[i * UPLOAD_DATA.length + j] = UPLOAD_DATA[j]; | 
| + } | 
| + } | 
| + connection.setFixedLengthStreamingMode(UPLOAD_DATA.length * REPEAT_COUNT); | 
| 
mmenke
2015/03/25 18:34:57
optional:  Suggest a method GetLargeData(), and us
 
xunjieli
2015/03/27 17:46:45
Done.
 | 
| + OutputStream out = connection.getOutputStream(); | 
| + int totalBytesWritten = 0; | 
| + // Number of bytes to write each time. It is incremented by one from 0. | 
| + int bytesToWrite = 0; | 
| + 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++; | 
| + } | 
| + assertEquals(200, connection.getResponseCode()); | 
| + assertEquals("OK", connection.getResponseMessage()); | 
| + checkLargeData(getResponseAsString(connection)); | 
| + connection.disconnect(); | 
| + } | 
| + | 
| + @SmallTest | 
| + @Feature({"Cronet"}) | 
| + @CompareDefaultWithCronet | 
| + public void testFixedLengthStreamingModeLargeDataWriteOneByte() | 
| + throws Exception { | 
| + URL url = new URL(NativeTestServer.getEchoBodyURL()); | 
| + HttpURLConnection connection = | 
| + (HttpURLConnection) url.openConnection(); | 
| + connection.setDoOutput(true); | 
| + connection.setRequestMethod("POST"); | 
| + | 
| + connection.setFixedLengthStreamingMode(UPLOAD_DATA.length * REPEAT_COUNT); | 
| + OutputStream out = connection.getOutputStream(); | 
| + for (int i = 0; i < REPEAT_COUNT; i++) { | 
| + for (int j = 0; j < UPLOAD_DATA.length; j++) { | 
| + // Write one byte at a time. | 
| + out.write(UPLOAD_DATA[j]); | 
| + } | 
| + } | 
| + assertEquals(200, connection.getResponseCode()); | 
| + assertEquals("OK", connection.getResponseMessage()); | 
| + checkLargeData(getResponseAsString(connection)); | 
| + connection.disconnect(); | 
| + } | 
| + | 
| + @SmallTest | 
| + @Feature({"Cronet"}) | 
| + @OnlyRunCronetHttpURLConnection | 
| + public void testLargeDataMoreThanNativeBufferSize() | 
| + throws Exception { | 
| + // Set an internal buffer of size larger than the buffer size used | 
| + // in network stack internally. | 
| + // Normal stream uses 16384, QUIC uses 14520, and SPDY uses 2852. | 
| + CronetOutputStream.setDefaultBufferLengthForTesting(17384); | 
| + testFixedLengthStreamingModeLargeDataWriteOneByte(); | 
| + testFixedLengthStreamingModeLargeData(); | 
| + } | 
| + | 
| + /** | 
| + * Helper method to extract response body as a string for testing. | 
| + */ | 
| + private String getResponseAsString(HttpURLConnection connection) | 
| + throws Exception { | 
| + InputStream in = connection.getInputStream(); | 
| + ByteArrayOutputStream out = new ByteArrayOutputStream(); | 
| + int b; | 
| + while ((b = in.read()) != -1) { | 
| + out.write(b); | 
| + } | 
| + return out.toString(); | 
| + } | 
| + | 
| + /** | 
| + * Helper function to check whether {@code data} is a concatenation of | 
| + * {@code REPEAT_COUNT} {@code UPLOAD_DATA_STRING} strings. | 
| + */ | 
| + private void checkLargeData(String data) { | 
| + for (int i = 0; i < REPEAT_COUNT; i++) { | 
| + assertEquals(UPLOAD_DATA_STRING, data.substring( | 
| + UPLOAD_DATA_STRING.length() * i, | 
| + UPLOAD_DATA_STRING.length() * (i + 1))); | 
| + } | 
| + } | 
| 
mmenke
2015/03/25 18:34:58
Maybe test a POST-preserving redirect, which shoul
 
xunjieli
2015/03/27 17:46:45
Done.
 | 
| +} |