Chromium Code Reviews| 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"), |