Pass CreateDirectory errors up to IndexedDB.
[chromium-blink-merge.git] / net / http / http_stream_parser_unittest.cc
blob580f43426ac4ac139666d82283546cc705cefe14
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/http/http_stream_parser.h"
7 #include "base/file_util.h"
8 #include "base/files/file_path.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/stringprintf.h"
13 #include "googleurl/src/gurl.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/test_completion_callback.h"
17 #include "net/base/upload_bytes_element_reader.h"
18 #include "net/base/upload_data_stream.h"
19 #include "net/base/upload_file_element_reader.h"
20 #include "net/http/http_request_headers.h"
21 #include "net/http/http_request_info.h"
22 #include "net/http/http_response_info.h"
23 #include "net/socket/client_socket_handle.h"
24 #include "net/socket/socket_test_util.h"
25 #include "testing/gtest/include/gtest/gtest.h"
27 namespace net {
29 const size_t kOutputSize = 1024; // Just large enough for this test.
30 // The number of bytes that can fit in a buffer of kOutputSize.
31 const size_t kMaxPayloadSize =
32 kOutputSize - HttpStreamParser::kChunkHeaderFooterSize;
34 // The empty payload is how the last chunk is encoded.
35 TEST(HttpStreamParser, EncodeChunk_EmptyPayload) {
36 char output[kOutputSize];
38 const base::StringPiece kPayload = "";
39 const base::StringPiece kExpected = "0\r\n\r\n";
40 const int num_bytes_written =
41 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
42 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
43 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
46 TEST(HttpStreamParser, EncodeChunk_ShortPayload) {
47 char output[kOutputSize];
49 const std::string kPayload("foo\x00\x11\x22", 6);
50 // 11 = payload size + sizeof("6") + CRLF x 2.
51 const std::string kExpected("6\r\nfoo\x00\x11\x22\r\n", 11);
52 const int num_bytes_written =
53 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
54 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
55 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
58 TEST(HttpStreamParser, EncodeChunk_LargePayload) {
59 char output[kOutputSize];
61 const std::string kPayload(1000, '\xff'); // '\xff' x 1000.
62 // 3E8 = 1000 in hex.
63 const std::string kExpected = "3E8\r\n" + kPayload + "\r\n";
64 const int num_bytes_written =
65 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
66 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
67 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
70 TEST(HttpStreamParser, EncodeChunk_FullPayload) {
71 char output[kOutputSize];
73 const std::string kPayload(kMaxPayloadSize, '\xff');
74 // 3F4 = 1012 in hex.
75 const std::string kExpected = "3F4\r\n" + kPayload + "\r\n";
76 const int num_bytes_written =
77 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
78 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
79 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
82 TEST(HttpStreamParser, EncodeChunk_TooLargePayload) {
83 char output[kOutputSize];
85 // The payload is one byte larger the output buffer size.
86 const std::string kPayload(kMaxPayloadSize + 1, '\xff');
87 const int num_bytes_written =
88 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
89 ASSERT_EQ(ERR_INVALID_ARGUMENT, num_bytes_written);
92 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_NoBody) {
93 // Shouldn't be merged if upload data is non-existent.
94 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
95 "some header", NULL));
98 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_EmptyBody) {
99 ScopedVector<UploadElementReader> element_readers;
100 scoped_ptr<UploadDataStream> body(new UploadDataStream(&element_readers, 0));
101 ASSERT_EQ(OK, body->Init(CompletionCallback()));
102 // Shouldn't be merged if upload data is empty.
103 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
104 "some header", body.get()));
107 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_ChunkedBody) {
108 const std::string payload = "123";
109 scoped_ptr<UploadDataStream> body(
110 new UploadDataStream(UploadDataStream::CHUNKED, 0));
111 body->AppendChunk(payload.data(), payload.size(), true);
112 ASSERT_EQ(OK, body->Init(CompletionCallback()));
113 // Shouldn't be merged if upload data carries chunked data.
114 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
115 "some header", body.get()));
118 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_FileBody) {
119 ScopedVector<UploadElementReader> element_readers;
121 // Create an empty temporary file.
122 base::ScopedTempDir temp_dir;
123 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
124 base::FilePath temp_file_path;
125 ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir.path(),
126 &temp_file_path));
128 element_readers.push_back(
129 new UploadFileElementReader(base::MessageLoopProxy::current().get(),
130 temp_file_path,
133 base::Time()));
135 scoped_ptr<UploadDataStream> body(new UploadDataStream(&element_readers, 0));
136 TestCompletionCallback callback;
137 ASSERT_EQ(ERR_IO_PENDING, body->Init(callback.callback()));
138 ASSERT_EQ(OK, callback.WaitForResult());
139 // Shouldn't be merged if upload data carries a file, as it's not in-memory.
140 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
141 "some header", body.get()));
144 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_SmallBodyInMemory) {
145 ScopedVector<UploadElementReader> element_readers;
146 const std::string payload = "123";
147 element_readers.push_back(new UploadBytesElementReader(
148 payload.data(), payload.size()));
150 scoped_ptr<UploadDataStream> body(new UploadDataStream(&element_readers, 0));
151 ASSERT_EQ(OK, body->Init(CompletionCallback()));
152 // Yes, should be merged if the in-memory body is small here.
153 ASSERT_TRUE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
154 "some header", body.get()));
157 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_LargeBodyInMemory) {
158 ScopedVector<UploadElementReader> element_readers;
159 const std::string payload(10000, 'a'); // 'a' x 10000.
160 element_readers.push_back(new UploadBytesElementReader(
161 payload.data(), payload.size()));
163 scoped_ptr<UploadDataStream> body(new UploadDataStream(&element_readers, 0));
164 ASSERT_EQ(OK, body->Init(CompletionCallback()));
165 // Shouldn't be merged if the in-memory body is large here.
166 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
167 "some header", body.get()));
170 // Test to ensure the HttpStreamParser state machine does not get confused
171 // when sending a request with a chunked body, where chunks become available
172 // asynchronously, over a socket where writes may also complete
173 // asynchronously.
174 // This is a regression test for http://crbug.com/132243
175 TEST(HttpStreamParser, AsyncChunkAndAsyncSocket) {
176 // The chunks that will be written in the request, as reflected in the
177 // MockWrites below.
178 static const char kChunk1[] = "Chunk 1";
179 static const char kChunk2[] = "Chunky 2";
180 static const char kChunk3[] = "Test 3";
182 MockWrite writes[] = {
183 MockWrite(ASYNC, 0,
184 "GET /one.html HTTP/1.1\r\n"
185 "Host: localhost\r\n"
186 "Transfer-Encoding: chunked\r\n"
187 "Connection: keep-alive\r\n\r\n"),
188 MockWrite(ASYNC, 1, "7\r\nChunk 1\r\n"),
189 MockWrite(ASYNC, 2, "8\r\nChunky 2\r\n"),
190 MockWrite(ASYNC, 3, "6\r\nTest 3\r\n"),
191 MockWrite(ASYNC, 4, "0\r\n\r\n"),
194 // The size of the response body, as reflected in the Content-Length of the
195 // MockRead below.
196 static const int kBodySize = 8;
198 MockRead reads[] = {
199 MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"),
200 MockRead(ASYNC, 6, "Content-Length: 8\r\n\r\n"),
201 MockRead(ASYNC, 7, "one.html"),
202 MockRead(SYNCHRONOUS, 0, 8), // EOF
205 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0);
206 upload_stream.AppendChunk(kChunk1, arraysize(kChunk1) - 1, false);
207 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback()));
209 DeterministicSocketData data(reads, arraysize(reads),
210 writes, arraysize(writes));
211 data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
213 scoped_ptr<DeterministicMockTCPClientSocket> transport(
214 new DeterministicMockTCPClientSocket(NULL, &data));
215 data.set_delegate(transport->AsWeakPtr());
217 TestCompletionCallback callback;
218 int rv = transport->Connect(callback.callback());
219 rv = callback.GetResult(rv);
220 ASSERT_EQ(OK, rv);
222 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
223 socket_handle->set_socket(transport.release());
225 HttpRequestInfo request_info;
226 request_info.method = "GET";
227 request_info.url = GURL("http://localhost");
228 request_info.load_flags = LOAD_NORMAL;
229 request_info.upload_data_stream = &upload_stream;
231 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
232 HttpStreamParser parser(
233 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog());
235 HttpRequestHeaders request_headers;
236 request_headers.SetHeader("Host", "localhost");
237 request_headers.SetHeader("Transfer-Encoding", "chunked");
238 request_headers.SetHeader("Connection", "keep-alive");
240 HttpResponseInfo response_info;
241 // This will attempt to Write() the initial request and headers, which will
242 // complete asynchronously.
243 rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
244 &response_info, callback.callback());
245 ASSERT_EQ(ERR_IO_PENDING, rv);
247 // Complete the initial request write. Additionally, this should enqueue the
248 // first chunk.
249 data.RunFor(1);
250 ASSERT_FALSE(callback.have_result());
252 // Now append another chunk (while the first write is still pending), which
253 // should not confuse the state machine.
254 upload_stream.AppendChunk(kChunk2, arraysize(kChunk2) - 1, false);
255 ASSERT_FALSE(callback.have_result());
257 // Complete writing the first chunk, which should then enqueue the second
258 // chunk for writing and return, because it is set to complete
259 // asynchronously.
260 data.RunFor(1);
261 ASSERT_FALSE(callback.have_result());
263 // Complete writing the second chunk. However, because no chunks are
264 // available yet, no further writes should be called until a new chunk is
265 // added.
266 data.RunFor(1);
267 ASSERT_FALSE(callback.have_result());
269 // Add the final chunk. This will enqueue another write, but it will not
270 // complete due to the async nature.
271 upload_stream.AppendChunk(kChunk3, arraysize(kChunk3) - 1, true);
272 ASSERT_FALSE(callback.have_result());
274 // Finalize writing the last chunk, which will enqueue the trailer.
275 data.RunFor(1);
276 ASSERT_FALSE(callback.have_result());
278 // Finalize writing the trailer.
279 data.RunFor(1);
280 ASSERT_TRUE(callback.have_result());
282 // Warning: This will hang if the callback doesn't already have a result,
283 // due to the deterministic socket provider. Do not remove the above
284 // ASSERT_TRUE, which will avoid this hang.
285 rv = callback.WaitForResult();
286 ASSERT_EQ(OK, rv);
288 // Attempt to read the response status and the response headers.
289 rv = parser.ReadResponseHeaders(callback.callback());
290 ASSERT_EQ(ERR_IO_PENDING, rv);
291 data.RunFor(2);
293 ASSERT_TRUE(callback.have_result());
294 rv = callback.WaitForResult();
295 ASSERT_GT(rv, 0);
297 // Finally, attempt to read the response body.
298 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
299 rv = parser.ReadResponseBody(
300 body_buffer.get(), kBodySize, callback.callback());
301 ASSERT_EQ(ERR_IO_PENDING, rv);
302 data.RunFor(1);
304 ASSERT_TRUE(callback.have_result());
305 rv = callback.WaitForResult();
306 ASSERT_EQ(kBodySize, rv);
309 TEST(HttpStreamParser, TruncatedHeaders) {
310 MockRead truncated_status_reads[] = {
311 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 20"),
312 MockRead(SYNCHRONOUS, 0, 2), // EOF
315 MockRead truncated_after_status_reads[] = {
316 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\n"),
317 MockRead(SYNCHRONOUS, 0, 2), // EOF
320 MockRead truncated_in_header_reads[] = {
321 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHead"),
322 MockRead(SYNCHRONOUS, 0, 2), // EOF
325 MockRead truncated_after_header_reads[] = {
326 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n"),
327 MockRead(SYNCHRONOUS, 0, 2), // EOF
330 MockRead truncated_after_final_newline_reads[] = {
331 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r"),
332 MockRead(SYNCHRONOUS, 0, 2), // EOF
335 MockRead not_truncated_reads[] = {
336 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r\n"),
337 MockRead(SYNCHRONOUS, 0, 2), // EOF
340 MockRead* reads[] = {
341 truncated_status_reads,
342 truncated_after_status_reads,
343 truncated_in_header_reads,
344 truncated_after_header_reads,
345 truncated_after_final_newline_reads,
346 not_truncated_reads,
349 MockWrite writes[] = {
350 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n\r\n"),
353 enum {
354 HTTP = 0,
355 HTTPS,
356 NUM_PROTOCOLS,
359 for (size_t protocol = 0; protocol < NUM_PROTOCOLS; protocol++) {
360 SCOPED_TRACE(protocol);
362 for (size_t i = 0; i < arraysize(reads); i++) {
363 SCOPED_TRACE(i);
364 DeterministicSocketData data(reads[i], 2, writes, arraysize(writes));
365 data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
366 data.SetStop(3);
368 scoped_ptr<DeterministicMockTCPClientSocket> transport(
369 new DeterministicMockTCPClientSocket(NULL, &data));
370 data.set_delegate(transport->AsWeakPtr());
372 TestCompletionCallback callback;
373 int rv = transport->Connect(callback.callback());
374 rv = callback.GetResult(rv);
375 ASSERT_EQ(OK, rv);
377 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
378 socket_handle->set_socket(transport.release());
380 HttpRequestInfo request_info;
381 request_info.method = "GET";
382 if (protocol == HTTP) {
383 request_info.url = GURL("http://localhost");
384 } else {
385 request_info.url = GURL("https://localhost");
387 request_info.load_flags = LOAD_NORMAL;
389 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
390 HttpStreamParser parser(
391 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog());
393 HttpRequestHeaders request_headers;
394 HttpResponseInfo response_info;
395 rv = parser.SendRequest("GET / HTTP/1.1\r\n", request_headers,
396 &response_info, callback.callback());
397 ASSERT_EQ(OK, rv);
399 rv = parser.ReadResponseHeaders(callback.callback());
400 if (i == arraysize(reads) - 1) {
401 EXPECT_EQ(OK, rv);
402 EXPECT_TRUE(response_info.headers.get());
403 } else {
404 if (protocol == HTTP) {
405 EXPECT_EQ(ERR_CONNECTION_CLOSED, rv);
406 EXPECT_TRUE(response_info.headers.get());
407 } else {
408 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED, rv);
409 EXPECT_FALSE(response_info.headers.get());
416 } // namespace net