OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "net/http/http_stream_parser.h" | |
6 | |
7 #include <algorithm> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "base/files/file_path.h" | |
12 #include "base/files/file_util.h" | |
13 #include "base/files/scoped_temp_dir.h" | |
14 #include "base/memory/ref_counted.h" | |
15 #include "base/run_loop.h" | |
16 #include "base/strings/string_piece.h" | |
17 #include "base/strings/stringprintf.h" | |
18 #include "net/base/chunked_upload_data_stream.h" | |
19 #include "net/base/elements_upload_data_stream.h" | |
20 #include "net/base/io_buffer.h" | |
21 #include "net/base/net_errors.h" | |
22 #include "net/base/test_completion_callback.h" | |
23 #include "net/base/upload_bytes_element_reader.h" | |
24 #include "net/base/upload_file_element_reader.h" | |
25 #include "net/http/http_request_headers.h" | |
26 #include "net/http/http_request_info.h" | |
27 #include "net/http/http_response_headers.h" | |
28 #include "net/http/http_response_info.h" | |
29 #include "net/socket/client_socket_handle.h" | |
30 #include "net/socket/socket_test_util.h" | |
31 #include "testing/gtest/include/gtest/gtest.h" | |
32 #include "url/gurl.h" | |
33 | |
34 namespace net { | |
35 | |
36 namespace { | |
37 | |
38 const size_t kOutputSize = 1024; // Just large enough for this test. | |
39 // The number of bytes that can fit in a buffer of kOutputSize. | |
40 const size_t kMaxPayloadSize = | |
41 kOutputSize - HttpStreamParser::kChunkHeaderFooterSize; | |
42 | |
43 // The empty payload is how the last chunk is encoded. | |
44 TEST(HttpStreamParser, EncodeChunk_EmptyPayload) { | |
45 char output[kOutputSize]; | |
46 | |
47 const base::StringPiece kPayload = ""; | |
48 const base::StringPiece kExpected = "0\r\n\r\n"; | |
49 const int num_bytes_written = | |
50 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); | |
51 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written)); | |
52 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written)); | |
53 } | |
54 | |
55 TEST(HttpStreamParser, EncodeChunk_ShortPayload) { | |
56 char output[kOutputSize]; | |
57 | |
58 const std::string kPayload("foo\x00\x11\x22", 6); | |
59 // 11 = payload size + sizeof("6") + CRLF x 2. | |
60 const std::string kExpected("6\r\nfoo\x00\x11\x22\r\n", 11); | |
61 const int num_bytes_written = | |
62 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); | |
63 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written)); | |
64 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written)); | |
65 } | |
66 | |
67 TEST(HttpStreamParser, EncodeChunk_LargePayload) { | |
68 char output[kOutputSize]; | |
69 | |
70 const std::string kPayload(1000, '\xff'); // '\xff' x 1000. | |
71 // 3E8 = 1000 in hex. | |
72 const std::string kExpected = "3E8\r\n" + kPayload + "\r\n"; | |
73 const int num_bytes_written = | |
74 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); | |
75 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written)); | |
76 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written)); | |
77 } | |
78 | |
79 TEST(HttpStreamParser, EncodeChunk_FullPayload) { | |
80 char output[kOutputSize]; | |
81 | |
82 const std::string kPayload(kMaxPayloadSize, '\xff'); | |
83 // 3F4 = 1012 in hex. | |
84 const std::string kExpected = "3F4\r\n" + kPayload + "\r\n"; | |
85 const int num_bytes_written = | |
86 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); | |
87 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written)); | |
88 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written)); | |
89 } | |
90 | |
91 TEST(HttpStreamParser, EncodeChunk_TooLargePayload) { | |
92 char output[kOutputSize]; | |
93 | |
94 // The payload is one byte larger the output buffer size. | |
95 const std::string kPayload(kMaxPayloadSize + 1, '\xff'); | |
96 const int num_bytes_written = | |
97 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); | |
98 ASSERT_EQ(ERR_INVALID_ARGUMENT, num_bytes_written); | |
99 } | |
100 | |
101 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_NoBody) { | |
102 // Shouldn't be merged if upload data is non-existent. | |
103 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( | |
104 "some header", NULL)); | |
105 } | |
106 | |
107 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_EmptyBody) { | |
108 ScopedVector<UploadElementReader> element_readers; | |
109 scoped_ptr<UploadDataStream> body( | |
110 new ElementsUploadDataStream(element_readers.Pass(), 0)); | |
111 ASSERT_EQ(OK, body->Init(CompletionCallback())); | |
112 // Shouldn't be merged if upload data is empty. | |
113 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( | |
114 "some header", body.get())); | |
115 } | |
116 | |
117 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_ChunkedBody) { | |
118 const std::string payload = "123"; | |
119 scoped_ptr<ChunkedUploadDataStream> body(new ChunkedUploadDataStream(0)); | |
120 body->AppendData(payload.data(), payload.size(), true); | |
121 ASSERT_EQ(OK, body->Init(TestCompletionCallback().callback())); | |
122 // Shouldn't be merged if upload data carries chunked data. | |
123 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( | |
124 "some header", body.get())); | |
125 } | |
126 | |
127 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_FileBody) { | |
128 { | |
129 ScopedVector<UploadElementReader> element_readers; | |
130 | |
131 // Create an empty temporary file. | |
132 base::ScopedTempDir temp_dir; | |
133 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
134 base::FilePath temp_file_path; | |
135 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), | |
136 &temp_file_path)); | |
137 | |
138 element_readers.push_back( | |
139 new UploadFileElementReader(base::MessageLoopProxy::current().get(), | |
140 temp_file_path, | |
141 0, | |
142 0, | |
143 base::Time())); | |
144 | |
145 scoped_ptr<UploadDataStream> body( | |
146 new ElementsUploadDataStream(element_readers.Pass(), 0)); | |
147 TestCompletionCallback callback; | |
148 ASSERT_EQ(ERR_IO_PENDING, body->Init(callback.callback())); | |
149 ASSERT_EQ(OK, callback.WaitForResult()); | |
150 // Shouldn't be merged if upload data carries a file, as it's not in-memory. | |
151 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( | |
152 "some header", body.get())); | |
153 } | |
154 // UploadFileElementReaders may post clean-up tasks on destruction. | |
155 base::RunLoop().RunUntilIdle(); | |
156 } | |
157 | |
158 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_SmallBodyInMemory) { | |
159 ScopedVector<UploadElementReader> element_readers; | |
160 const std::string payload = "123"; | |
161 element_readers.push_back(new UploadBytesElementReader( | |
162 payload.data(), payload.size())); | |
163 | |
164 scoped_ptr<UploadDataStream> body( | |
165 new ElementsUploadDataStream(element_readers.Pass(), 0)); | |
166 ASSERT_EQ(OK, body->Init(CompletionCallback())); | |
167 // Yes, should be merged if the in-memory body is small here. | |
168 ASSERT_TRUE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( | |
169 "some header", body.get())); | |
170 } | |
171 | |
172 TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_LargeBodyInMemory) { | |
173 ScopedVector<UploadElementReader> element_readers; | |
174 const std::string payload(10000, 'a'); // 'a' x 10000. | |
175 element_readers.push_back(new UploadBytesElementReader( | |
176 payload.data(), payload.size())); | |
177 | |
178 scoped_ptr<UploadDataStream> body( | |
179 new ElementsUploadDataStream(element_readers.Pass(), 0)); | |
180 ASSERT_EQ(OK, body->Init(CompletionCallback())); | |
181 // Shouldn't be merged if the in-memory body is large here. | |
182 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( | |
183 "some header", body.get())); | |
184 } | |
185 | |
186 // Test to ensure the HttpStreamParser state machine does not get confused | |
187 // when sending a request with a chunked body, where chunks become available | |
188 // asynchronously, over a socket where writes may also complete | |
189 // asynchronously. | |
190 // This is a regression test for http://crbug.com/132243 | |
191 TEST(HttpStreamParser, AsyncChunkAndAsyncSocket) { | |
192 // The chunks that will be written in the request, as reflected in the | |
193 // MockWrites below. | |
194 static const char kChunk1[] = "Chunk 1"; | |
195 static const char kChunk2[] = "Chunky 2"; | |
196 static const char kChunk3[] = "Test 3"; | |
197 | |
198 MockWrite writes[] = { | |
199 MockWrite(ASYNC, 0, | |
200 "GET /one.html HTTP/1.1\r\n" | |
201 "Host: localhost\r\n" | |
202 "Transfer-Encoding: chunked\r\n" | |
203 "Connection: keep-alive\r\n\r\n"), | |
204 MockWrite(ASYNC, 1, "7\r\nChunk 1\r\n"), | |
205 MockWrite(ASYNC, 2, "8\r\nChunky 2\r\n"), | |
206 MockWrite(ASYNC, 3, "6\r\nTest 3\r\n"), | |
207 MockWrite(ASYNC, 4, "0\r\n\r\n"), | |
208 }; | |
209 | |
210 // The size of the response body, as reflected in the Content-Length of the | |
211 // MockRead below. | |
212 static const int kBodySize = 8; | |
213 | |
214 MockRead reads[] = { | |
215 MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"), | |
216 MockRead(ASYNC, 6, "Content-Length: 8\r\n\r\n"), | |
217 MockRead(ASYNC, 7, "one.html"), | |
218 MockRead(SYNCHRONOUS, 0, 8), // EOF | |
219 }; | |
220 | |
221 ChunkedUploadDataStream upload_stream(0); | |
222 upload_stream.AppendData(kChunk1, arraysize(kChunk1) - 1, false); | |
223 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback())); | |
224 | |
225 DeterministicSocketData data(reads, arraysize(reads), | |
226 writes, arraysize(writes)); | |
227 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); | |
228 | |
229 scoped_ptr<DeterministicMockTCPClientSocket> transport( | |
230 new DeterministicMockTCPClientSocket(NULL, &data)); | |
231 data.set_delegate(transport->AsWeakPtr()); | |
232 | |
233 TestCompletionCallback callback; | |
234 int rv = transport->Connect(callback.callback()); | |
235 rv = callback.GetResult(rv); | |
236 ASSERT_EQ(OK, rv); | |
237 | |
238 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); | |
239 socket_handle->SetSocket(transport.Pass()); | |
240 | |
241 HttpRequestInfo request_info; | |
242 request_info.method = "GET"; | |
243 request_info.url = GURL("http://localhost"); | |
244 request_info.load_flags = LOAD_NORMAL; | |
245 request_info.upload_data_stream = &upload_stream; | |
246 | |
247 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); | |
248 HttpStreamParser parser( | |
249 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog()); | |
250 | |
251 HttpRequestHeaders request_headers; | |
252 request_headers.SetHeader("Host", "localhost"); | |
253 request_headers.SetHeader("Transfer-Encoding", "chunked"); | |
254 request_headers.SetHeader("Connection", "keep-alive"); | |
255 | |
256 HttpResponseInfo response_info; | |
257 // This will attempt to Write() the initial request and headers, which will | |
258 // complete asynchronously. | |
259 rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers, | |
260 &response_info, callback.callback()); | |
261 ASSERT_EQ(ERR_IO_PENDING, rv); | |
262 | |
263 // Complete the initial request write. Additionally, this should enqueue the | |
264 // first chunk. | |
265 data.RunFor(1); | |
266 ASSERT_FALSE(callback.have_result()); | |
267 | |
268 // Now append another chunk (while the first write is still pending), which | |
269 // should not confuse the state machine. | |
270 upload_stream.AppendData(kChunk2, arraysize(kChunk2) - 1, false); | |
271 ASSERT_FALSE(callback.have_result()); | |
272 | |
273 // Complete writing the first chunk, which should then enqueue the second | |
274 // chunk for writing and return, because it is set to complete | |
275 // asynchronously. | |
276 data.RunFor(1); | |
277 ASSERT_FALSE(callback.have_result()); | |
278 | |
279 // Complete writing the second chunk. However, because no chunks are | |
280 // available yet, no further writes should be called until a new chunk is | |
281 // added. | |
282 data.RunFor(1); | |
283 ASSERT_FALSE(callback.have_result()); | |
284 | |
285 // Add the final chunk. This will enqueue another write, but it will not | |
286 // complete due to the async nature. | |
287 upload_stream.AppendData(kChunk3, arraysize(kChunk3) - 1, true); | |
288 ASSERT_FALSE(callback.have_result()); | |
289 | |
290 // Finalize writing the last chunk, which will enqueue the trailer. | |
291 data.RunFor(1); | |
292 ASSERT_FALSE(callback.have_result()); | |
293 | |
294 // Finalize writing the trailer. | |
295 data.RunFor(1); | |
296 ASSERT_TRUE(callback.have_result()); | |
297 | |
298 // Warning: This will hang if the callback doesn't already have a result, | |
299 // due to the deterministic socket provider. Do not remove the above | |
300 // ASSERT_TRUE, which will avoid this hang. | |
301 rv = callback.WaitForResult(); | |
302 ASSERT_EQ(OK, rv); | |
303 | |
304 // Attempt to read the response status and the response headers. | |
305 rv = parser.ReadResponseHeaders(callback.callback()); | |
306 ASSERT_EQ(ERR_IO_PENDING, rv); | |
307 data.RunFor(2); | |
308 | |
309 ASSERT_TRUE(callback.have_result()); | |
310 rv = callback.WaitForResult(); | |
311 ASSERT_GT(rv, 0); | |
312 | |
313 // Finally, attempt to read the response body. | |
314 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); | |
315 rv = parser.ReadResponseBody( | |
316 body_buffer.get(), kBodySize, callback.callback()); | |
317 ASSERT_EQ(ERR_IO_PENDING, rv); | |
318 data.RunFor(1); | |
319 | |
320 ASSERT_TRUE(callback.have_result()); | |
321 rv = callback.WaitForResult(); | |
322 ASSERT_EQ(kBodySize, rv); | |
323 } | |
324 | |
325 TEST(HttpStreamParser, TruncatedHeaders) { | |
326 MockRead truncated_status_reads[] = { | |
327 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 20"), | |
328 MockRead(SYNCHRONOUS, 0, 2), // EOF | |
329 }; | |
330 | |
331 MockRead truncated_after_status_reads[] = { | |
332 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\n"), | |
333 MockRead(SYNCHRONOUS, 0, 2), // EOF | |
334 }; | |
335 | |
336 MockRead truncated_in_header_reads[] = { | |
337 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHead"), | |
338 MockRead(SYNCHRONOUS, 0, 2), // EOF | |
339 }; | |
340 | |
341 MockRead truncated_after_header_reads[] = { | |
342 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n"), | |
343 MockRead(SYNCHRONOUS, 0, 2), // EOF | |
344 }; | |
345 | |
346 MockRead truncated_after_final_newline_reads[] = { | |
347 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r"), | |
348 MockRead(SYNCHRONOUS, 0, 2), // EOF | |
349 }; | |
350 | |
351 MockRead not_truncated_reads[] = { | |
352 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r\n"), | |
353 MockRead(SYNCHRONOUS, 0, 2), // EOF | |
354 }; | |
355 | |
356 MockRead* reads[] = { | |
357 truncated_status_reads, | |
358 truncated_after_status_reads, | |
359 truncated_in_header_reads, | |
360 truncated_after_header_reads, | |
361 truncated_after_final_newline_reads, | |
362 not_truncated_reads, | |
363 }; | |
364 | |
365 MockWrite writes[] = { | |
366 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n\r\n"), | |
367 }; | |
368 | |
369 enum { | |
370 HTTP = 0, | |
371 HTTPS, | |
372 NUM_PROTOCOLS, | |
373 }; | |
374 | |
375 for (size_t protocol = 0; protocol < NUM_PROTOCOLS; protocol++) { | |
376 SCOPED_TRACE(protocol); | |
377 | |
378 for (size_t i = 0; i < arraysize(reads); i++) { | |
379 SCOPED_TRACE(i); | |
380 DeterministicSocketData data(reads[i], 2, writes, arraysize(writes)); | |
381 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); | |
382 data.SetStop(3); | |
383 | |
384 scoped_ptr<DeterministicMockTCPClientSocket> transport( | |
385 new DeterministicMockTCPClientSocket(NULL, &data)); | |
386 data.set_delegate(transport->AsWeakPtr()); | |
387 | |
388 TestCompletionCallback callback; | |
389 int rv = transport->Connect(callback.callback()); | |
390 rv = callback.GetResult(rv); | |
391 ASSERT_EQ(OK, rv); | |
392 | |
393 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); | |
394 socket_handle->SetSocket(transport.Pass()); | |
395 | |
396 HttpRequestInfo request_info; | |
397 request_info.method = "GET"; | |
398 if (protocol == HTTP) { | |
399 request_info.url = GURL("http://localhost"); | |
400 } else { | |
401 request_info.url = GURL("https://localhost"); | |
402 } | |
403 request_info.load_flags = LOAD_NORMAL; | |
404 | |
405 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); | |
406 HttpStreamParser parser( | |
407 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog()); | |
408 | |
409 HttpRequestHeaders request_headers; | |
410 HttpResponseInfo response_info; | |
411 rv = parser.SendRequest("GET / HTTP/1.1\r\n", request_headers, | |
412 &response_info, callback.callback()); | |
413 ASSERT_EQ(OK, rv); | |
414 | |
415 rv = parser.ReadResponseHeaders(callback.callback()); | |
416 if (i == arraysize(reads) - 1) { | |
417 EXPECT_EQ(OK, rv); | |
418 EXPECT_TRUE(response_info.headers.get()); | |
419 } else { | |
420 if (protocol == HTTP) { | |
421 EXPECT_EQ(ERR_CONNECTION_CLOSED, rv); | |
422 EXPECT_TRUE(response_info.headers.get()); | |
423 } else { | |
424 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED, rv); | |
425 EXPECT_FALSE(response_info.headers.get()); | |
426 } | |
427 } | |
428 } | |
429 } | |
430 } | |
431 | |
432 // Confirm that on 101 response, the headers are parsed but the data that | |
433 // follows remains in the buffer. | |
434 TEST(HttpStreamParser, Websocket101Response) { | |
435 MockRead reads[] = { | |
436 MockRead(SYNCHRONOUS, 1, | |
437 "HTTP/1.1 101 Switching Protocols\r\n" | |
438 "Upgrade: websocket\r\n" | |
439 "Connection: Upgrade\r\n" | |
440 "\r\n" | |
441 "a fake websocket frame"), | |
442 }; | |
443 | |
444 MockWrite writes[] = { | |
445 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n\r\n"), | |
446 }; | |
447 | |
448 DeterministicSocketData data(reads, arraysize(reads), | |
449 writes, arraysize(writes)); | |
450 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); | |
451 data.SetStop(2); | |
452 | |
453 scoped_ptr<DeterministicMockTCPClientSocket> transport( | |
454 new DeterministicMockTCPClientSocket(NULL, &data)); | |
455 data.set_delegate(transport->AsWeakPtr()); | |
456 | |
457 TestCompletionCallback callback; | |
458 int rv = transport->Connect(callback.callback()); | |
459 rv = callback.GetResult(rv); | |
460 ASSERT_EQ(OK, rv); | |
461 | |
462 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); | |
463 socket_handle->SetSocket(transport.Pass()); | |
464 | |
465 HttpRequestInfo request_info; | |
466 request_info.method = "GET"; | |
467 request_info.url = GURL("http://localhost"); | |
468 request_info.load_flags = LOAD_NORMAL; | |
469 | |
470 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); | |
471 HttpStreamParser parser( | |
472 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog()); | |
473 | |
474 HttpRequestHeaders request_headers; | |
475 HttpResponseInfo response_info; | |
476 rv = parser.SendRequest("GET / HTTP/1.1\r\n", request_headers, | |
477 &response_info, callback.callback()); | |
478 ASSERT_EQ(OK, rv); | |
479 | |
480 rv = parser.ReadResponseHeaders(callback.callback()); | |
481 EXPECT_EQ(OK, rv); | |
482 ASSERT_TRUE(response_info.headers.get()); | |
483 EXPECT_EQ(101, response_info.headers->response_code()); | |
484 EXPECT_TRUE(response_info.headers->HasHeaderValue("Connection", "Upgrade")); | |
485 EXPECT_TRUE(response_info.headers->HasHeaderValue("Upgrade", "websocket")); | |
486 EXPECT_EQ(read_buffer->capacity(), read_buffer->offset()); | |
487 EXPECT_EQ("a fake websocket frame", | |
488 base::StringPiece(read_buffer->StartOfBuffer(), | |
489 read_buffer->capacity())); | |
490 } | |
491 | |
492 // Helper class for constructing HttpStreamParser and running GET requests. | |
493 class SimpleGetRunner { | |
494 public: | |
495 SimpleGetRunner() : read_buffer_(new GrowableIOBuffer), sequence_number_(0) { | |
496 writes_.push_back(MockWrite( | |
497 SYNCHRONOUS, sequence_number_++, "GET / HTTP/1.1\r\n\r\n")); | |
498 } | |
499 | |
500 HttpStreamParser* parser() { return parser_.get(); } | |
501 GrowableIOBuffer* read_buffer() { return read_buffer_.get(); } | |
502 HttpResponseInfo* response_info() { return &response_info_; } | |
503 | |
504 void AddInitialData(const std::string& data) { | |
505 int offset = read_buffer_->offset(); | |
506 int size = data.size(); | |
507 read_buffer_->SetCapacity(offset + size); | |
508 memcpy(read_buffer_->StartOfBuffer() + offset, data.data(), size); | |
509 read_buffer_->set_offset(offset + size); | |
510 } | |
511 | |
512 void AddRead(const std::string& data) { | |
513 reads_.push_back(MockRead(SYNCHRONOUS, sequence_number_++, data.data())); | |
514 } | |
515 | |
516 void SetupParserAndSendRequest() { | |
517 reads_.push_back(MockRead(SYNCHRONOUS, 0, sequence_number_++)); // EOF | |
518 | |
519 socket_handle_.reset(new ClientSocketHandle); | |
520 data_.reset(new DeterministicSocketData( | |
521 &reads_.front(), reads_.size(), &writes_.front(), writes_.size())); | |
522 data_->set_connect_data(MockConnect(SYNCHRONOUS, OK)); | |
523 data_->SetStop(reads_.size() + writes_.size()); | |
524 | |
525 transport_.reset(new DeterministicMockTCPClientSocket(NULL, data_.get())); | |
526 data_->set_delegate(transport_->AsWeakPtr()); | |
527 | |
528 TestCompletionCallback callback; | |
529 int rv = transport_->Connect(callback.callback()); | |
530 rv = callback.GetResult(rv); | |
531 ASSERT_EQ(OK, rv); | |
532 | |
533 socket_handle_->SetSocket(transport_.Pass()); | |
534 | |
535 request_info_.method = "GET"; | |
536 request_info_.url = GURL("http://localhost"); | |
537 request_info_.load_flags = LOAD_NORMAL; | |
538 | |
539 parser_.reset(new HttpStreamParser( | |
540 socket_handle_.get(), &request_info_, read_buffer(), BoundNetLog())); | |
541 | |
542 rv = parser_->SendRequest("GET / HTTP/1.1\r\n", request_headers_, | |
543 &response_info_, callback.callback()); | |
544 ASSERT_EQ(OK, rv); | |
545 } | |
546 | |
547 void ReadHeaders() { | |
548 TestCompletionCallback callback; | |
549 EXPECT_EQ(OK, parser_->ReadResponseHeaders(callback.callback())); | |
550 } | |
551 | |
552 void ReadBody(int user_buf_len, int* read_lengths) { | |
553 TestCompletionCallback callback; | |
554 scoped_refptr<IOBuffer> buffer = new IOBuffer(user_buf_len); | |
555 int rv; | |
556 int i = 0; | |
557 while (true) { | |
558 rv = parser_->ReadResponseBody( | |
559 buffer.get(), user_buf_len, callback.callback()); | |
560 EXPECT_EQ(read_lengths[i], rv); | |
561 i++; | |
562 if (rv <= 0) | |
563 return; | |
564 } | |
565 } | |
566 | |
567 private: | |
568 HttpRequestHeaders request_headers_; | |
569 HttpResponseInfo response_info_; | |
570 HttpRequestInfo request_info_; | |
571 scoped_refptr<GrowableIOBuffer> read_buffer_; | |
572 std::vector<MockRead> reads_; | |
573 std::vector<MockWrite> writes_; | |
574 scoped_ptr<ClientSocketHandle> socket_handle_; | |
575 scoped_ptr<DeterministicSocketData> data_; | |
576 scoped_ptr<DeterministicMockTCPClientSocket> transport_; | |
577 scoped_ptr<HttpStreamParser> parser_; | |
578 int sequence_number_; | |
579 }; | |
580 | |
581 // Test that HTTP/0.9 response size is correctly calculated. | |
582 TEST(HttpStreamParser, ReceivedBytesNoHeaders) { | |
583 std::string response = "hello\r\nworld\r\n"; | |
584 | |
585 SimpleGetRunner get_runner; | |
586 get_runner.AddRead(response); | |
587 get_runner.SetupParserAndSendRequest(); | |
588 get_runner.ReadHeaders(); | |
589 EXPECT_EQ(0, get_runner.parser()->received_bytes()); | |
590 int response_size = response.size(); | |
591 int read_lengths[] = {response_size, 0}; | |
592 get_runner.ReadBody(response_size, read_lengths); | |
593 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); | |
594 } | |
595 | |
596 // Test basic case where there is no keep-alive or extra data from the socket, | |
597 // and the entire response is received in a single read. | |
598 TEST(HttpStreamParser, ReceivedBytesNormal) { | |
599 std::string headers = "HTTP/1.1 200 OK\r\n" | |
600 "Content-Length: 7\r\n\r\n"; | |
601 std::string body = "content"; | |
602 std::string response = headers + body; | |
603 | |
604 SimpleGetRunner get_runner; | |
605 get_runner.AddRead(response); | |
606 get_runner.SetupParserAndSendRequest(); | |
607 get_runner.ReadHeaders(); | |
608 int64 headers_size = headers.size(); | |
609 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); | |
610 int body_size = body.size(); | |
611 int read_lengths[] = {body_size, 0}; | |
612 get_runner.ReadBody(body_size, read_lengths); | |
613 int64 response_size = response.size(); | |
614 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); | |
615 } | |
616 | |
617 // Test that bytes that represent "next" response are not counted | |
618 // as current response "received_bytes". | |
619 TEST(HttpStreamParser, ReceivedBytesExcludesNextResponse) { | |
620 std::string headers = "HTTP/1.1 200 OK\r\n" | |
621 "Content-Length: 8\r\n\r\n"; | |
622 std::string body = "content8"; | |
623 std::string response = headers + body; | |
624 std::string next_response = "HTTP/1.1 200 OK\r\n\r\nFOO"; | |
625 std::string data = response + next_response; | |
626 | |
627 SimpleGetRunner get_runner; | |
628 get_runner.AddRead(data); | |
629 get_runner.SetupParserAndSendRequest(); | |
630 get_runner.ReadHeaders(); | |
631 EXPECT_EQ(39, get_runner.parser()->received_bytes()); | |
632 int64 headers_size = headers.size(); | |
633 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); | |
634 int body_size = body.size(); | |
635 int read_lengths[] = {body_size, 0}; | |
636 get_runner.ReadBody(body_size, read_lengths); | |
637 int64 response_size = response.size(); | |
638 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); | |
639 int64 next_response_size = next_response.size(); | |
640 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset()); | |
641 } | |
642 | |
643 // Test that "received_bytes" calculation works fine when last read | |
644 // contains more data than requested by user. | |
645 // We send data in two reads: | |
646 // 1) Headers + beginning of response | |
647 // 2) remaining part of response + next response start | |
648 // We setup user read buffer so it fully accepts the beginnig of response | |
649 // body, but it is larger that remaining part of body. | |
650 TEST(HttpStreamParser, ReceivedBytesMultiReadExcludesNextResponse) { | |
651 std::string headers = "HTTP/1.1 200 OK\r\n" | |
652 "Content-Length: 36\r\n\r\n"; | |
653 int64 user_buf_len = 32; | |
654 std::string body_start = std::string(user_buf_len, '#'); | |
655 int body_start_size = body_start.size(); | |
656 EXPECT_EQ(user_buf_len, body_start_size); | |
657 std::string response_start = headers + body_start; | |
658 std::string body_end = "abcd"; | |
659 std::string next_response = "HTTP/1.1 200 OK\r\n\r\nFOO"; | |
660 std::string response_end = body_end + next_response; | |
661 | |
662 SimpleGetRunner get_runner; | |
663 get_runner.AddRead(response_start); | |
664 get_runner.AddRead(response_end); | |
665 get_runner.SetupParserAndSendRequest(); | |
666 get_runner.ReadHeaders(); | |
667 int64 headers_size = headers.size(); | |
668 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); | |
669 int body_end_size = body_end.size(); | |
670 int read_lengths[] = {body_start_size, body_end_size, 0}; | |
671 get_runner.ReadBody(body_start_size, read_lengths); | |
672 int64 response_size = response_start.size() + body_end_size; | |
673 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); | |
674 int64 next_response_size = next_response.size(); | |
675 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset()); | |
676 } | |
677 | |
678 // Test that "received_bytes" calculation works fine when there is no | |
679 // network activity at all; that is when all data is read from read buffer. | |
680 // In this case read buffer contains two responses. We expect that only | |
681 // bytes that correspond to the first one are taken into account. | |
682 TEST(HttpStreamParser, ReceivedBytesFromReadBufExcludesNextResponse) { | |
683 std::string headers = "HTTP/1.1 200 OK\r\n" | |
684 "Content-Length: 7\r\n\r\n"; | |
685 std::string body = "content"; | |
686 std::string response = headers + body; | |
687 std::string next_response = "HTTP/1.1 200 OK\r\n\r\nFOO"; | |
688 std::string data = response + next_response; | |
689 | |
690 SimpleGetRunner get_runner; | |
691 get_runner.AddInitialData(data); | |
692 get_runner.SetupParserAndSendRequest(); | |
693 get_runner.ReadHeaders(); | |
694 int64 headers_size = headers.size(); | |
695 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); | |
696 int body_size = body.size(); | |
697 int read_lengths[] = {body_size, 0}; | |
698 get_runner.ReadBody(body_size, read_lengths); | |
699 int64 response_size = response.size(); | |
700 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); | |
701 int64 next_response_size = next_response.size(); | |
702 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset()); | |
703 } | |
704 | |
705 // Test calculating "received_bytes" when part of request has been already | |
706 // loaded and placed to read buffer by previous stream parser. | |
707 TEST(HttpStreamParser, ReceivedBytesUseReadBuf) { | |
708 std::string buffer = "HTTP/1.1 200 OK\r\n"; | |
709 std::string remaining_headers = "Content-Length: 7\r\n\r\n"; | |
710 int64 headers_size = buffer.size() + remaining_headers.size(); | |
711 std::string body = "content"; | |
712 std::string response = remaining_headers + body; | |
713 | |
714 SimpleGetRunner get_runner; | |
715 get_runner.AddInitialData(buffer); | |
716 get_runner.AddRead(response); | |
717 get_runner.SetupParserAndSendRequest(); | |
718 get_runner.ReadHeaders(); | |
719 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); | |
720 int body_size = body.size(); | |
721 int read_lengths[] = {body_size, 0}; | |
722 get_runner.ReadBody(body_size, read_lengths); | |
723 EXPECT_EQ(headers_size + body_size, get_runner.parser()->received_bytes()); | |
724 EXPECT_EQ(0, get_runner.read_buffer()->offset()); | |
725 } | |
726 | |
727 // Test the case when the resulting read_buf contains both unused bytes and | |
728 // bytes ejected by chunked-encoding filter. | |
729 TEST(HttpStreamParser, ReceivedBytesChunkedTransferExcludesNextResponse) { | |
730 std::string response = "HTTP/1.1 200 OK\r\n" | |
731 "Transfer-Encoding: chunked\r\n\r\n" | |
732 "7\r\nChunk 1\r\n" | |
733 "8\r\nChunky 2\r\n" | |
734 "6\r\nTest 3\r\n" | |
735 "0\r\n\r\n"; | |
736 std::string next_response = "foo bar\r\n"; | |
737 std::string data = response + next_response; | |
738 | |
739 SimpleGetRunner get_runner; | |
740 get_runner.AddInitialData(data); | |
741 get_runner.SetupParserAndSendRequest(); | |
742 get_runner.ReadHeaders(); | |
743 int read_lengths[] = {4, 3, 6, 2, 6, 0}; | |
744 get_runner.ReadBody(7, read_lengths); | |
745 int64 response_size = response.size(); | |
746 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); | |
747 int64 next_response_size = next_response.size(); | |
748 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset()); | |
749 } | |
750 | |
751 // Test that data transfered in multiple reads is correctly processed. | |
752 // We feed data into 4-bytes reads. Also we set length of read | |
753 // buffer to 5-bytes to test all possible buffer misaligments. | |
754 TEST(HttpStreamParser, ReceivedBytesMultipleReads) { | |
755 std::string headers = "HTTP/1.1 200 OK\r\n" | |
756 "Content-Length: 33\r\n\r\n"; | |
757 std::string body = "foo bar baz\r\n" | |
758 "sputnik mir babushka"; | |
759 std::string response = headers + body; | |
760 | |
761 size_t receive_length = 4; | |
762 std::vector<std::string> blocks; | |
763 for (size_t i = 0; i < response.size(); i += receive_length) { | |
764 size_t length = std::min(receive_length, response.size() - i); | |
765 blocks.push_back(response.substr(i, length)); | |
766 } | |
767 | |
768 SimpleGetRunner get_runner; | |
769 for (std::vector<std::string>::size_type i = 0; i < blocks.size(); ++i) | |
770 get_runner.AddRead(blocks[i]); | |
771 get_runner.SetupParserAndSendRequest(); | |
772 get_runner.ReadHeaders(); | |
773 int64 headers_size = headers.size(); | |
774 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); | |
775 int read_lengths[] = {1, 4, 4, 4, 4, 4, 4, 4, 4, 0}; | |
776 get_runner.ReadBody(receive_length + 1, read_lengths); | |
777 int64 response_size = response.size(); | |
778 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); | |
779 } | |
780 | |
781 // Test that "continue" HTTP header is counted as "received_bytes". | |
782 TEST(HttpStreamParser, ReceivedBytesIncludesContinueHeader) { | |
783 std::string status100 = "HTTP/1.1 100 OK\r\n\r\n"; | |
784 std::string headers = "HTTP/1.1 200 OK\r\n" | |
785 "Content-Length: 7\r\n\r\n"; | |
786 int64 headers_size = status100.size() + headers.size(); | |
787 std::string body = "content"; | |
788 std::string response = headers + body; | |
789 | |
790 SimpleGetRunner get_runner; | |
791 get_runner.AddRead(status100); | |
792 get_runner.AddRead(response); | |
793 get_runner.SetupParserAndSendRequest(); | |
794 get_runner.ReadHeaders(); | |
795 EXPECT_EQ(100, get_runner.response_info()->headers->response_code()); | |
796 int64 status100_size = status100.size(); | |
797 EXPECT_EQ(status100_size, get_runner.parser()->received_bytes()); | |
798 get_runner.ReadHeaders(); | |
799 EXPECT_EQ(200, get_runner.response_info()->headers->response_code()); | |
800 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); | |
801 int64 response_size = headers_size + body.size(); | |
802 int body_size = body.size(); | |
803 int read_lengths[] = {body_size, 0}; | |
804 get_runner.ReadBody(body_size, read_lengths); | |
805 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); | |
806 } | |
807 | |
808 // Test that an HttpStreamParser can be read from after it's received headers | |
809 // and data structures owned by its owner have been deleted. This happens | |
810 // when a ResponseBodyDrainer is used. | |
811 TEST(HttpStreamParser, ReadAfterUnownedObjectsDestroyed) { | |
812 MockWrite writes[] = { | |
813 MockWrite(SYNCHRONOUS, 0, | |
814 "GET /foo.html HTTP/1.1\r\n\r\n"), | |
815 MockWrite(SYNCHRONOUS, 1, "1"), | |
816 }; | |
817 | |
818 const int kBodySize = 1; | |
819 MockRead reads[] = { | |
820 MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), | |
821 MockRead(SYNCHRONOUS, 6, "Content-Length: 1\r\n\r\n"), | |
822 MockRead(SYNCHRONOUS, 6, "Connection: Keep-Alive\r\n\r\n"), | |
823 MockRead(SYNCHRONOUS, 7, "1"), | |
824 MockRead(SYNCHRONOUS, 0, 8), // EOF | |
825 }; | |
826 | |
827 StaticSocketDataProvider data(reads, arraysize(reads), writes, | |
828 arraysize(writes)); | |
829 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); | |
830 | |
831 scoped_ptr<MockTCPClientSocket> transport( | |
832 new MockTCPClientSocket(AddressList(), NULL, &data)); | |
833 | |
834 TestCompletionCallback callback; | |
835 ASSERT_EQ(OK, transport->Connect(callback.callback())); | |
836 | |
837 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); | |
838 socket_handle->SetSocket(transport.Pass()); | |
839 | |
840 scoped_ptr<HttpRequestInfo> request_info(new HttpRequestInfo()); | |
841 request_info->method = "GET"; | |
842 request_info->url = GURL("http://somewhere/foo.html"); | |
843 | |
844 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); | |
845 HttpStreamParser parser(socket_handle.get(), request_info.get(), | |
846 read_buffer.get(), BoundNetLog()); | |
847 | |
848 scoped_ptr<HttpRequestHeaders> request_headers(new HttpRequestHeaders()); | |
849 scoped_ptr<HttpResponseInfo> response_info(new HttpResponseInfo()); | |
850 ASSERT_EQ(OK, parser.SendRequest("GET /foo.html HTTP/1.1\r\n", | |
851 *request_headers, response_info.get(), callback.callback())); | |
852 ASSERT_EQ(OK, parser.ReadResponseHeaders(callback.callback())); | |
853 | |
854 // If the object that owns the HttpStreamParser is deleted, it takes the | |
855 // objects passed to the HttpStreamParser with it. | |
856 request_info.reset(); | |
857 request_headers.reset(); | |
858 response_info.reset(); | |
859 | |
860 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); | |
861 ASSERT_EQ(kBodySize, parser.ReadResponseBody( | |
862 body_buffer.get(), kBodySize, callback.callback())); | |
863 } | |
864 | |
865 } // namespace | |
866 | |
867 } // namespace net | |
OLD | NEW |