Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1093)

Unified Diff: net/http/http_stream_parser_unittest.cc

Issue 921453003: Allow URLRequest::AppendChunkToUpload to take 0-byte final chunks. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | net/url_request/url_request.cc » ('j') | net/url_request/url_request.cc » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/http/http_stream_parser_unittest.cc
diff --git a/net/http/http_stream_parser_unittest.cc b/net/http/http_stream_parser_unittest.cc
index 01d4e120ab1b583bae90aef127db07654e16224b..aa45b71373560682bec527e367e25156e7b9a7e8 100644
--- a/net/http/http_stream_parser_unittest.cc
+++ b/net/http/http_stream_parser_unittest.cc
@@ -322,6 +322,316 @@ TEST(HttpStreamParser, AsyncChunkAndAsyncSocket) {
ASSERT_EQ(kBodySize, rv);
}
+// Test to ensure the HttpStreamParser state machine does not get confused
+// when the final "chunk" has 0 bytes, and is received from the UploadStream
+// only after sending other chunk.
+TEST(HttpStreamParser, AsyncChunkWithEmptyFinalChunk) {
+ const char kChunk[] = "Chunk";
+
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0,
+ "GET /one.html HTTP/1.1\r\n"
+ "Host: localhost\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ MockWrite(ASYNC, 1, "5\r\nChunk\r\n"),
+ MockWrite(ASYNC, 2, "0\r\n\r\n"),
+ };
+
+ // The size of the response body, as reflected in the Content-Length of the
+ // MockRead below.
+ const int kBodySize = 8;
+
+ MockRead reads[] = {
+ MockRead(ASYNC, 3, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 4, "Content-Length: 8\r\n\r\n"),
+ MockRead(ASYNC, 5, "one.html"),
+ MockRead(SYNCHRONOUS, 0, 6), // EOF
+ };
+
+ ChunkedUploadDataStream upload_stream(0);
+ upload_stream.AppendData(kChunk, arraysize(kChunk) - 1, false);
+ ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
+
+ DeterministicSocketData data(reads, arraysize(reads), writes,
+ arraysize(writes));
+ data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+
+ scoped_ptr<DeterministicMockTCPClientSocket> transport(
+ new DeterministicMockTCPClientSocket(NULL, &data));
+ data.set_delegate(transport->AsWeakPtr());
+
+ TestCompletionCallback callback;
+ int rv = transport->Connect(callback.callback());
+ rv = callback.GetResult(rv);
+ ASSERT_EQ(OK, rv);
+
+ scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
+ socket_handle->SetSocket(transport.Pass());
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://localhost");
+ request_info.load_flags = LOAD_NORMAL;
+ request_info.upload_data_stream = &upload_stream;
mef 2015/02/11 21:32:10 is it possible to set upload_data_stream to stream
mmenke 2015/02/11 22:37:22 Yes...SyncEmptyChunkedUpload is the only test in t
+
+ scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
+ HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
+ BoundNetLog());
+
+ HttpRequestHeaders request_headers;
+ request_headers.SetHeader("Host", "localhost");
+ request_headers.SetHeader("Transfer-Encoding", "chunked");
+ request_headers.SetHeader("Connection", "keep-alive");
+
+ HttpResponseInfo response_info;
+ // This will attempt to Write() the initial request and headers, which will
+ // complete asynchronously.
+ rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
+ &response_info, callback.callback());
+ ASSERT_EQ(ERR_IO_PENDING, rv);
+
+ // Complete the initial request write and the chunk with data.
+ data.RunFor(2);
+ ASSERT_FALSE(callback.have_result());
+
+ // Now append the terminal 0-byte "chunk".
+ upload_stream.AppendData(nullptr, 0, true);
+ ASSERT_FALSE(callback.have_result());
+
+ // Finalize writing the trailer.
+ data.RunFor(1);
+ ASSERT_TRUE(callback.have_result());
+
+ // Warning: This will hang if the callback doesn't already have a result,
+ // due to the deterministic socket provider. Do not remove the above
+ // ASSERT_TRUE, which will avoid this hang.
+ rv = callback.WaitForResult();
+ ASSERT_EQ(OK, rv);
+
+ // Attempt to read the response status and the response headers.
+ rv = parser.ReadResponseHeaders(callback.callback());
+ ASSERT_EQ(ERR_IO_PENDING, rv);
+ data.RunFor(2);
+
+ ASSERT_TRUE(callback.have_result());
+ rv = callback.WaitForResult();
+ ASSERT_GT(rv, 0);
+
+ // Finally, attempt to read the response body.
+ scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
+ rv = parser.ReadResponseBody(body_buffer.get(), kBodySize,
+ callback.callback());
+ ASSERT_EQ(ERR_IO_PENDING, rv);
+ data.RunFor(1);
+
+ ASSERT_TRUE(callback.have_result());
+ rv = callback.WaitForResult();
+ ASSERT_EQ(kBodySize, rv);
+}
+
+// Test to ensure the HttpStreamParser state machine does not get confused
+// when there's only one "chunk" with 0 bytes, and is received from the
+// UploadStream only after sending the request headers successfully.
+TEST(HttpStreamParser, AsyncEmptyChunkedUpload) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0,
+ "GET /one.html HTTP/1.1\r\n"
+ "Host: localhost\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ MockWrite(ASYNC, 1, "0\r\n\r\n"),
+ };
+
+ // The size of the response body, as reflected in the Content-Length of the
+ // MockRead below.
+ const int kBodySize = 8;
+
+ MockRead reads[] = {
+ MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 3, "Content-Length: 8\r\n\r\n"),
+ MockRead(ASYNC, 4, "one.html"),
+ MockRead(SYNCHRONOUS, 0, 5), // EOF
+ };
+
+ ChunkedUploadDataStream upload_stream(0);
+ ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
+
+ DeterministicSocketData data(reads, arraysize(reads), writes,
+ arraysize(writes));
+ data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+
+ scoped_ptr<DeterministicMockTCPClientSocket> transport(
+ new DeterministicMockTCPClientSocket(NULL, &data));
+ data.set_delegate(transport->AsWeakPtr());
+
+ TestCompletionCallback callback;
+ int rv = transport->Connect(callback.callback());
+ rv = callback.GetResult(rv);
+ ASSERT_EQ(OK, rv);
+
+ scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
+ socket_handle->SetSocket(transport.Pass());
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://localhost");
+ request_info.load_flags = LOAD_NORMAL;
+ request_info.upload_data_stream = &upload_stream;
+
+ scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
+ HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
+ BoundNetLog());
+
+ HttpRequestHeaders request_headers;
+ request_headers.SetHeader("Host", "localhost");
+ request_headers.SetHeader("Transfer-Encoding", "chunked");
+ request_headers.SetHeader("Connection", "keep-alive");
+
+ HttpResponseInfo response_info;
+ // This will attempt to Write() the initial request and headers, which will
+ // complete asynchronously.
+ rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
+ &response_info, callback.callback());
+ ASSERT_EQ(ERR_IO_PENDING, rv);
+
+ // Complete writing the request headers.
+ data.RunFor(1);
+ ASSERT_FALSE(callback.have_result());
+
+ // Now append the terminal 0-byte "chunk".
+ upload_stream.AppendData(nullptr, 0, true);
+ ASSERT_FALSE(callback.have_result());
+
+ // Finalize writing the trailer.
+ data.RunFor(1);
+ ASSERT_TRUE(callback.have_result());
+
+ // Warning: This will hang if the callback doesn't already have a result,
+ // due to the deterministic socket provider. Do not remove the above
+ // ASSERT_TRUE, which will avoid this hang.
+ rv = callback.WaitForResult();
+ ASSERT_EQ(OK, rv);
+
+ // Attempt to read the response status and the response headers.
+ rv = parser.ReadResponseHeaders(callback.callback());
+ ASSERT_EQ(ERR_IO_PENDING, rv);
+ data.RunFor(2);
+
+ ASSERT_TRUE(callback.have_result());
+ rv = callback.WaitForResult();
+ ASSERT_GT(rv, 0);
+
+ // Finally, attempt to read the response body.
+ scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
+ rv = parser.ReadResponseBody(body_buffer.get(), kBodySize,
+ callback.callback());
+ ASSERT_EQ(ERR_IO_PENDING, rv);
+ data.RunFor(1);
+
+ ASSERT_TRUE(callback.have_result());
+ rv = callback.WaitForResult();
+ ASSERT_EQ(kBodySize, rv);
+}
+
+// Test to ensure the HttpStreamParser state machine does not get confused
+// when there's only one "chunk" with 0 bytes, which was already appended before
+// the request was started.
+TEST(HttpStreamParser, SyncEmptyChunkedUpload) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0,
+ "GET /one.html HTTP/1.1\r\n"
+ "Host: localhost\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ MockWrite(ASYNC, 1, "0\r\n\r\n"),
+ };
+
+ // The size of the response body, as reflected in the Content-Length of the
+ // MockRead below.
+ const int kBodySize = 8;
+
+ MockRead reads[] = {
+ MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 3, "Content-Length: 8\r\n\r\n"),
+ MockRead(ASYNC, 4, "one.html"),
+ MockRead(SYNCHRONOUS, 0, 5), // EOF
+ };
+
+ ChunkedUploadDataStream upload_stream(0);
+ ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
+ // Append final empty chunk.
+ upload_stream.AppendData(nullptr, 0, true);
+
+ DeterministicSocketData data(reads, arraysize(reads), writes,
+ arraysize(writes));
+ data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+
+ scoped_ptr<DeterministicMockTCPClientSocket> transport(
+ new DeterministicMockTCPClientSocket(NULL, &data));
+ data.set_delegate(transport->AsWeakPtr());
+
+ TestCompletionCallback callback;
+ int rv = transport->Connect(callback.callback());
+ rv = callback.GetResult(rv);
+ ASSERT_EQ(OK, rv);
+
+ scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
+ socket_handle->SetSocket(transport.Pass());
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://localhost");
+ request_info.load_flags = LOAD_NORMAL;
+ request_info.upload_data_stream = &upload_stream;
+
+ scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
+ HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
+ BoundNetLog());
+
+ HttpRequestHeaders request_headers;
+ request_headers.SetHeader("Host", "localhost");
+ request_headers.SetHeader("Transfer-Encoding", "chunked");
+ request_headers.SetHeader("Connection", "keep-alive");
+
+ HttpResponseInfo response_info;
+ // This will attempt to Write() the initial request and headers, which will
+ // complete asynchronously.
+ rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
+ &response_info, callback.callback());
+ ASSERT_EQ(ERR_IO_PENDING, rv);
+
+ // Complete writing the request headers and body.
+ data.RunFor(2);
+ ASSERT_TRUE(callback.have_result());
+
+ // Warning: This will hang if the callback doesn't already have a result,
+ // due to the deterministic socket provider. Do not remove the above
+ // ASSERT_TRUE, which will avoid this hang.
+ rv = callback.WaitForResult();
+ ASSERT_EQ(OK, rv);
+
+ // Attempt to read the response status and the response headers.
+ rv = parser.ReadResponseHeaders(callback.callback());
+ ASSERT_EQ(ERR_IO_PENDING, rv);
+ data.RunFor(2);
+
+ ASSERT_TRUE(callback.have_result());
+ rv = callback.WaitForResult();
+ ASSERT_GT(rv, 0);
+
+ // Finally, attempt to read the response body.
+ scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
+ rv = parser.ReadResponseBody(body_buffer.get(), kBodySize,
+ callback.callback());
+ ASSERT_EQ(ERR_IO_PENDING, rv);
+ data.RunFor(1);
+
+ ASSERT_TRUE(callback.have_result());
+ rv = callback.WaitForResult();
+ ASSERT_EQ(kBodySize, rv);
+}
+
TEST(HttpStreamParser, TruncatedHeaders) {
MockRead truncated_status_reads[] = {
MockRead(SYNCHRONOUS, 1, "HTTP/1.1 20"),
« no previous file with comments | « no previous file | net/url_request/url_request.cc » ('j') | net/url_request/url_request.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698