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"
11 #include "base/file_util.h"
12 #include "base/files/file_path.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/io_buffer.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/test_completion_callback.h"
21 #include "net/base/upload_bytes_element_reader.h"
22 #include "net/base/upload_data_stream.h"
23 #include "net/base/upload_file_element_reader.h"
24 #include "net/http/http_request_headers.h"
25 #include "net/http/http_request_info.h"
26 #include "net/http/http_response_headers.h"
27 #include "net/http/http_response_info.h"
28 #include "net/socket/client_socket_handle.h"
29 #include "net/socket/socket_test_util.h"
30 #include "testing/gtest/include/gtest/gtest.h"
37 const size_t kOutputSize
= 1024; // Just large enough for this test.
38 // The number of bytes that can fit in a buffer of kOutputSize.
39 const size_t kMaxPayloadSize
=
40 kOutputSize
- HttpStreamParser::kChunkHeaderFooterSize
;
42 // The empty payload is how the last chunk is encoded.
43 TEST(HttpStreamParser
, EncodeChunk_EmptyPayload
) {
44 char output
[kOutputSize
];
46 const base::StringPiece kPayload
= "";
47 const base::StringPiece kExpected
= "0\r\n\r\n";
48 const int num_bytes_written
=
49 HttpStreamParser::EncodeChunk(kPayload
, output
, sizeof(output
));
50 ASSERT_EQ(kExpected
.size(), static_cast<size_t>(num_bytes_written
));
51 EXPECT_EQ(kExpected
, base::StringPiece(output
, num_bytes_written
));
54 TEST(HttpStreamParser
, EncodeChunk_ShortPayload
) {
55 char output
[kOutputSize
];
57 const std::string
kPayload("foo\x00\x11\x22", 6);
58 // 11 = payload size + sizeof("6") + CRLF x 2.
59 const std::string
kExpected("6\r\nfoo\x00\x11\x22\r\n", 11);
60 const int num_bytes_written
=
61 HttpStreamParser::EncodeChunk(kPayload
, output
, sizeof(output
));
62 ASSERT_EQ(kExpected
.size(), static_cast<size_t>(num_bytes_written
));
63 EXPECT_EQ(kExpected
, base::StringPiece(output
, num_bytes_written
));
66 TEST(HttpStreamParser
, EncodeChunk_LargePayload
) {
67 char output
[kOutputSize
];
69 const std::string
kPayload(1000, '\xff'); // '\xff' x 1000.
71 const std::string kExpected
= "3E8\r\n" + kPayload
+ "\r\n";
72 const int num_bytes_written
=
73 HttpStreamParser::EncodeChunk(kPayload
, output
, sizeof(output
));
74 ASSERT_EQ(kExpected
.size(), static_cast<size_t>(num_bytes_written
));
75 EXPECT_EQ(kExpected
, base::StringPiece(output
, num_bytes_written
));
78 TEST(HttpStreamParser
, EncodeChunk_FullPayload
) {
79 char output
[kOutputSize
];
81 const std::string
kPayload(kMaxPayloadSize
, '\xff');
83 const std::string kExpected
= "3F4\r\n" + kPayload
+ "\r\n";
84 const int num_bytes_written
=
85 HttpStreamParser::EncodeChunk(kPayload
, output
, sizeof(output
));
86 ASSERT_EQ(kExpected
.size(), static_cast<size_t>(num_bytes_written
));
87 EXPECT_EQ(kExpected
, base::StringPiece(output
, num_bytes_written
));
90 TEST(HttpStreamParser
, EncodeChunk_TooLargePayload
) {
91 char output
[kOutputSize
];
93 // The payload is one byte larger the output buffer size.
94 const std::string
kPayload(kMaxPayloadSize
+ 1, '\xff');
95 const int num_bytes_written
=
96 HttpStreamParser::EncodeChunk(kPayload
, output
, sizeof(output
));
97 ASSERT_EQ(ERR_INVALID_ARGUMENT
, num_bytes_written
);
100 TEST(HttpStreamParser
, ShouldMergeRequestHeadersAndBody_NoBody
) {
101 // Shouldn't be merged if upload data is non-existent.
102 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
103 "some header", NULL
));
106 TEST(HttpStreamParser
, ShouldMergeRequestHeadersAndBody_EmptyBody
) {
107 ScopedVector
<UploadElementReader
> element_readers
;
108 scoped_ptr
<UploadDataStream
> body(
109 new UploadDataStream(element_readers
.Pass(), 0));
110 ASSERT_EQ(OK
, body
->Init(CompletionCallback()));
111 // Shouldn't be merged if upload data is empty.
112 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
113 "some header", body
.get()));
116 TEST(HttpStreamParser
, ShouldMergeRequestHeadersAndBody_ChunkedBody
) {
117 const std::string payload
= "123";
118 scoped_ptr
<UploadDataStream
> body(
119 new UploadDataStream(UploadDataStream::CHUNKED
, 0));
120 body
->AppendChunk(payload
.data(), payload
.size(), true);
121 ASSERT_EQ(OK
, body
->Init(CompletionCallback()));
122 // Shouldn't be merged if upload data carries chunked data.
123 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
124 "some header", body
.get()));
127 TEST(HttpStreamParser
, ShouldMergeRequestHeadersAndBody_FileBody
) {
129 ScopedVector
<UploadElementReader
> element_readers
;
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(),
138 element_readers
.push_back(
139 new UploadFileElementReader(base::MessageLoopProxy::current().get(),
145 scoped_ptr
<UploadDataStream
> body(
146 new UploadDataStream(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()));
154 // UploadFileElementReaders may post clean-up tasks on destruction.
155 base::RunLoop().RunUntilIdle();
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()));
164 scoped_ptr
<UploadDataStream
> body(
165 new UploadDataStream(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()));
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()));
178 scoped_ptr
<UploadDataStream
> body(
179 new UploadDataStream(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()));
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
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
194 static const char kChunk1
[] = "Chunk 1";
195 static const char kChunk2
[] = "Chunky 2";
196 static const char kChunk3
[] = "Test 3";
198 MockWrite writes
[] = {
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"),
210 // The size of the response body, as reflected in the Content-Length of the
212 static const int kBodySize
= 8;
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
221 UploadDataStream
upload_stream(UploadDataStream::CHUNKED
, 0);
222 upload_stream
.AppendChunk(kChunk1
, arraysize(kChunk1
) - 1, false);
223 ASSERT_EQ(OK
, upload_stream
.Init(CompletionCallback()));
225 DeterministicSocketData
data(reads
, arraysize(reads
),
226 writes
, arraysize(writes
));
227 data
.set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
229 scoped_ptr
<DeterministicMockTCPClientSocket
> transport(
230 new DeterministicMockTCPClientSocket(NULL
, &data
));
231 data
.set_delegate(transport
->AsWeakPtr());
233 TestCompletionCallback callback
;
234 int rv
= transport
->Connect(callback
.callback());
235 rv
= callback
.GetResult(rv
);
238 scoped_ptr
<ClientSocketHandle
> socket_handle(new ClientSocketHandle
);
239 socket_handle
->SetSocket(transport
.PassAs
<StreamSocket
>());
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
;
247 scoped_refptr
<GrowableIOBuffer
> read_buffer(new GrowableIOBuffer
);
248 HttpStreamParser
parser(
249 socket_handle
.get(), &request_info
, read_buffer
.get(), BoundNetLog());
251 HttpRequestHeaders request_headers
;
252 request_headers
.SetHeader("Host", "localhost");
253 request_headers
.SetHeader("Transfer-Encoding", "chunked");
254 request_headers
.SetHeader("Connection", "keep-alive");
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
);
263 // Complete the initial request write. Additionally, this should enqueue the
266 ASSERT_FALSE(callback
.have_result());
268 // Now append another chunk (while the first write is still pending), which
269 // should not confuse the state machine.
270 upload_stream
.AppendChunk(kChunk2
, arraysize(kChunk2
) - 1, false);
271 ASSERT_FALSE(callback
.have_result());
273 // Complete writing the first chunk, which should then enqueue the second
274 // chunk for writing and return, because it is set to complete
277 ASSERT_FALSE(callback
.have_result());
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
283 ASSERT_FALSE(callback
.have_result());
285 // Add the final chunk. This will enqueue another write, but it will not
286 // complete due to the async nature.
287 upload_stream
.AppendChunk(kChunk3
, arraysize(kChunk3
) - 1, true);
288 ASSERT_FALSE(callback
.have_result());
290 // Finalize writing the last chunk, which will enqueue the trailer.
292 ASSERT_FALSE(callback
.have_result());
294 // Finalize writing the trailer.
296 ASSERT_TRUE(callback
.have_result());
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();
304 // Attempt to read the response status and the response headers.
305 rv
= parser
.ReadResponseHeaders(callback
.callback());
306 ASSERT_EQ(ERR_IO_PENDING
, rv
);
309 ASSERT_TRUE(callback
.have_result());
310 rv
= callback
.WaitForResult();
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
);
320 ASSERT_TRUE(callback
.have_result());
321 rv
= callback
.WaitForResult();
322 ASSERT_EQ(kBodySize
, rv
);
325 TEST(HttpStreamParser
, TruncatedHeaders
) {
326 MockRead truncated_status_reads
[] = {
327 MockRead(SYNCHRONOUS
, 1, "HTTP/1.1 20"),
328 MockRead(SYNCHRONOUS
, 0, 2), // EOF
331 MockRead truncated_after_status_reads
[] = {
332 MockRead(SYNCHRONOUS
, 1, "HTTP/1.1 200 Ok\r\n"),
333 MockRead(SYNCHRONOUS
, 0, 2), // EOF
336 MockRead truncated_in_header_reads
[] = {
337 MockRead(SYNCHRONOUS
, 1, "HTTP/1.1 200 Ok\r\nHead"),
338 MockRead(SYNCHRONOUS
, 0, 2), // EOF
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
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
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
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
,
365 MockWrite writes
[] = {
366 MockWrite(SYNCHRONOUS
, 0, "GET / HTTP/1.1\r\n\r\n"),
375 for (size_t protocol
= 0; protocol
< NUM_PROTOCOLS
; protocol
++) {
376 SCOPED_TRACE(protocol
);
378 for (size_t i
= 0; i
< arraysize(reads
); i
++) {
380 DeterministicSocketData
data(reads
[i
], 2, writes
, arraysize(writes
));
381 data
.set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
384 scoped_ptr
<DeterministicMockTCPClientSocket
> transport(
385 new DeterministicMockTCPClientSocket(NULL
, &data
));
386 data
.set_delegate(transport
->AsWeakPtr());
388 TestCompletionCallback callback
;
389 int rv
= transport
->Connect(callback
.callback());
390 rv
= callback
.GetResult(rv
);
393 scoped_ptr
<ClientSocketHandle
> socket_handle(new ClientSocketHandle
);
394 socket_handle
->SetSocket(transport
.PassAs
<StreamSocket
>());
396 HttpRequestInfo request_info
;
397 request_info
.method
= "GET";
398 if (protocol
== HTTP
) {
399 request_info
.url
= GURL("http://localhost");
401 request_info
.url
= GURL("https://localhost");
403 request_info
.load_flags
= LOAD_NORMAL
;
405 scoped_refptr
<GrowableIOBuffer
> read_buffer(new GrowableIOBuffer
);
406 HttpStreamParser
parser(
407 socket_handle
.get(), &request_info
, read_buffer
.get(), BoundNetLog());
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());
415 rv
= parser
.ReadResponseHeaders(callback
.callback());
416 if (i
== arraysize(reads
) - 1) {
418 EXPECT_TRUE(response_info
.headers
.get());
420 if (protocol
== HTTP
) {
421 EXPECT_EQ(ERR_CONNECTION_CLOSED
, rv
);
422 EXPECT_TRUE(response_info
.headers
.get());
424 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED
, rv
);
425 EXPECT_FALSE(response_info
.headers
.get());
432 // Confirm that on 101 response, the headers are parsed but the data that
433 // follows remains in the buffer.
434 TEST(HttpStreamParser
, Websocket101Response
) {
436 MockRead(SYNCHRONOUS
, 1,
437 "HTTP/1.1 101 Switching Protocols\r\n"
438 "Upgrade: websocket\r\n"
439 "Connection: Upgrade\r\n"
441 "a fake websocket frame"),
444 MockWrite writes
[] = {
445 MockWrite(SYNCHRONOUS
, 0, "GET / HTTP/1.1\r\n\r\n"),
448 DeterministicSocketData
data(reads
, arraysize(reads
),
449 writes
, arraysize(writes
));
450 data
.set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
453 scoped_ptr
<DeterministicMockTCPClientSocket
> transport(
454 new DeterministicMockTCPClientSocket(NULL
, &data
));
455 data
.set_delegate(transport
->AsWeakPtr());
457 TestCompletionCallback callback
;
458 int rv
= transport
->Connect(callback
.callback());
459 rv
= callback
.GetResult(rv
);
462 scoped_ptr
<ClientSocketHandle
> socket_handle(new ClientSocketHandle
);
463 socket_handle
->SetSocket(transport
.PassAs
<StreamSocket
>());
465 HttpRequestInfo request_info
;
466 request_info
.method
= "GET";
467 request_info
.url
= GURL("http://localhost");
468 request_info
.load_flags
= LOAD_NORMAL
;
470 scoped_refptr
<GrowableIOBuffer
> read_buffer(new GrowableIOBuffer
);
471 HttpStreamParser
parser(
472 socket_handle
.get(), &request_info
, read_buffer
.get(), BoundNetLog());
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());
480 rv
= parser
.ReadResponseHeaders(callback
.callback());
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()));
492 // Helper class for constructing HttpStreamParser and running GET requests.
493 class SimpleGetRunner
{
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"));
500 HttpStreamParser
* parser() { return parser_
.get(); }
501 GrowableIOBuffer
* read_buffer() { return read_buffer_
.get(); }
502 HttpResponseInfo
* response_info() { return &response_info_
; }
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
);
512 void AddRead(const std::string
& data
) {
513 reads_
.push_back(MockRead(SYNCHRONOUS
, sequence_number_
++, data
.data()));
516 void SetupParserAndSendRequest() {
517 reads_
.push_back(MockRead(SYNCHRONOUS
, 0, sequence_number_
++)); // EOF
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());
525 transport_
.reset(new DeterministicMockTCPClientSocket(NULL
, data_
.get()));
526 data_
->set_delegate(transport_
->AsWeakPtr());
528 TestCompletionCallback callback
;
529 int rv
= transport_
->Connect(callback
.callback());
530 rv
= callback
.GetResult(rv
);
533 socket_handle_
->SetSocket(transport_
.PassAs
<StreamSocket
>());
535 request_info_
.method
= "GET";
536 request_info_
.url
= GURL("http://localhost");
537 request_info_
.load_flags
= LOAD_NORMAL
;
539 parser_
.reset(new HttpStreamParser(
540 socket_handle_
.get(), &request_info_
, read_buffer(), BoundNetLog()));
542 rv
= parser_
->SendRequest("GET / HTTP/1.1\r\n", request_headers_
,
543 &response_info_
, callback
.callback());
548 TestCompletionCallback callback
;
549 EXPECT_EQ(OK
, parser_
->ReadResponseHeaders(callback
.callback()));
552 void ReadBody(int user_buf_len
, int* read_lengths
) {
553 TestCompletionCallback callback
;
554 scoped_refptr
<IOBuffer
> buffer
= new IOBuffer(user_buf_len
);
558 rv
= parser_
->ReadResponseBody(buffer
, user_buf_len
, callback
.callback());
559 EXPECT_EQ(read_lengths
[i
], rv
);
567 HttpRequestHeaders request_headers_
;
568 HttpResponseInfo response_info_
;
569 HttpRequestInfo request_info_
;
570 scoped_refptr
<GrowableIOBuffer
> read_buffer_
;
571 std::vector
<MockRead
> reads_
;
572 std::vector
<MockWrite
> writes_
;
573 scoped_ptr
<ClientSocketHandle
> socket_handle_
;
574 scoped_ptr
<DeterministicSocketData
> data_
;
575 scoped_ptr
<DeterministicMockTCPClientSocket
> transport_
;
576 scoped_ptr
<HttpStreamParser
> parser_
;
577 int sequence_number_
;
580 // Test that HTTP/0.9 response size is correctly calculated.
581 TEST(HttpStreamParser
, ReceivedBytesNoHeaders
) {
582 std::string response
= "hello\r\nworld\r\n";
584 SimpleGetRunner get_runner
;
585 get_runner
.AddRead(response
);
586 get_runner
.SetupParserAndSendRequest();
587 get_runner
.ReadHeaders();
588 EXPECT_EQ(0, get_runner
.parser()->received_bytes());
589 int response_size
= response
.size();
590 int read_lengths
[] = {response_size
, 0};
591 get_runner
.ReadBody(response_size
, read_lengths
);
592 EXPECT_EQ(response_size
, get_runner
.parser()->received_bytes());
595 // Test basic case where there is no keep-alive or extra data from the socket,
596 // and the entire response is received in a single read.
597 TEST(HttpStreamParser
, ReceivedBytesNormal
) {
598 std::string headers
= "HTTP/1.1 200 OK\r\n"
599 "Content-Length: 7\r\n\r\n";
600 std::string body
= "content";
601 std::string response
= headers
+ body
;
603 SimpleGetRunner get_runner
;
604 get_runner
.AddRead(response
);
605 get_runner
.SetupParserAndSendRequest();
606 get_runner
.ReadHeaders();
607 int64 headers_size
= headers
.size();
608 EXPECT_EQ(headers_size
, get_runner
.parser()->received_bytes());
609 int body_size
= body
.size();
610 int read_lengths
[] = {body_size
, 0};
611 get_runner
.ReadBody(body_size
, read_lengths
);
612 int64 response_size
= response
.size();
613 EXPECT_EQ(response_size
, get_runner
.parser()->received_bytes());
616 // Test that bytes that represent "next" response are not counted
617 // as current response "received_bytes".
618 TEST(HttpStreamParser
, ReceivedBytesExcludesNextResponse
) {
619 std::string headers
= "HTTP/1.1 200 OK\r\n"
620 "Content-Length: 8\r\n\r\n";
621 std::string body
= "content8";
622 std::string response
= headers
+ body
;
623 std::string next_response
= "HTTP/1.1 200 OK\r\n\r\nFOO";
624 std::string data
= response
+ next_response
;
626 SimpleGetRunner get_runner
;
627 get_runner
.AddRead(data
);
628 get_runner
.SetupParserAndSendRequest();
629 get_runner
.ReadHeaders();
630 EXPECT_EQ(39, get_runner
.parser()->received_bytes());
631 int64 headers_size
= headers
.size();
632 EXPECT_EQ(headers_size
, get_runner
.parser()->received_bytes());
633 int body_size
= body
.size();
634 int read_lengths
[] = {body_size
, 0};
635 get_runner
.ReadBody(body_size
, read_lengths
);
636 int64 response_size
= response
.size();
637 EXPECT_EQ(response_size
, get_runner
.parser()->received_bytes());
638 int64 next_response_size
= next_response
.size();
639 EXPECT_EQ(next_response_size
, get_runner
.read_buffer()->offset());
642 // Test that "received_bytes" calculation works fine when last read
643 // contains more data than requested by user.
644 // We send data in two reads:
645 // 1) Headers + beginning of response
646 // 2) remaining part of response + next response start
647 // We setup user read buffer so it fully accepts the beginnig of response
648 // body, but it is larger that remaining part of body.
649 TEST(HttpStreamParser
, ReceivedBytesMultiReadExcludesNextResponse
) {
650 std::string headers
= "HTTP/1.1 200 OK\r\n"
651 "Content-Length: 36\r\n\r\n";
652 int64 user_buf_len
= 32;
653 std::string body_start
= std::string(user_buf_len
, '#');
654 int body_start_size
= body_start
.size();
655 EXPECT_EQ(user_buf_len
, body_start_size
);
656 std::string response_start
= headers
+ body_start
;
657 std::string body_end
= "abcd";
658 std::string next_response
= "HTTP/1.1 200 OK\r\n\r\nFOO";
659 std::string response_end
= body_end
+ next_response
;
661 SimpleGetRunner get_runner
;
662 get_runner
.AddRead(response_start
);
663 get_runner
.AddRead(response_end
);
664 get_runner
.SetupParserAndSendRequest();
665 get_runner
.ReadHeaders();
666 int64 headers_size
= headers
.size();
667 EXPECT_EQ(headers_size
, get_runner
.parser()->received_bytes());
668 int body_end_size
= body_end
.size();
669 int read_lengths
[] = {body_start_size
, body_end_size
, 0};
670 get_runner
.ReadBody(body_start_size
, read_lengths
);
671 int64 response_size
= response_start
.size() + body_end_size
;
672 EXPECT_EQ(response_size
, get_runner
.parser()->received_bytes());
673 int64 next_response_size
= next_response
.size();
674 EXPECT_EQ(next_response_size
, get_runner
.read_buffer()->offset());
677 // Test that "received_bytes" calculation works fine when there is no
678 // network activity at all; that is when all data is read from read buffer.
679 // In this case read buffer contains two responses. We expect that only
680 // bytes that correspond to the first one are taken into account.
681 TEST(HttpStreamParser
, ReceivedBytesFromReadBufExcludesNextResponse
) {
682 std::string headers
= "HTTP/1.1 200 OK\r\n"
683 "Content-Length: 7\r\n\r\n";
684 std::string body
= "content";
685 std::string response
= headers
+ body
;
686 std::string next_response
= "HTTP/1.1 200 OK\r\n\r\nFOO";
687 std::string data
= response
+ next_response
;
689 SimpleGetRunner get_runner
;
690 get_runner
.AddInitialData(data
);
691 get_runner
.SetupParserAndSendRequest();
692 get_runner
.ReadHeaders();
693 int64 headers_size
= headers
.size();
694 EXPECT_EQ(headers_size
, get_runner
.parser()->received_bytes());
695 int body_size
= body
.size();
696 int read_lengths
[] = {body_size
, 0};
697 get_runner
.ReadBody(body_size
, read_lengths
);
698 int64 response_size
= response
.size();
699 EXPECT_EQ(response_size
, get_runner
.parser()->received_bytes());
700 int64 next_response_size
= next_response
.size();
701 EXPECT_EQ(next_response_size
, get_runner
.read_buffer()->offset());
704 // Test calculating "received_bytes" when part of request has been already
705 // loaded and placed to read buffer by previous stream parser.
706 TEST(HttpStreamParser
, ReceivedBytesUseReadBuf
) {
707 std::string buffer
= "HTTP/1.1 200 OK\r\n";
708 std::string remaining_headers
= "Content-Length: 7\r\n\r\n";
709 int64 headers_size
= buffer
.size() + remaining_headers
.size();
710 std::string body
= "content";
711 std::string response
= remaining_headers
+ body
;
713 SimpleGetRunner get_runner
;
714 get_runner
.AddInitialData(buffer
);
715 get_runner
.AddRead(response
);
716 get_runner
.SetupParserAndSendRequest();
717 get_runner
.ReadHeaders();
718 EXPECT_EQ(headers_size
, get_runner
.parser()->received_bytes());
719 int body_size
= body
.size();
720 int read_lengths
[] = {body_size
, 0};
721 get_runner
.ReadBody(body_size
, read_lengths
);
722 EXPECT_EQ(headers_size
+ body_size
, get_runner
.parser()->received_bytes());
723 EXPECT_EQ(0, get_runner
.read_buffer()->offset());
726 // Test the case when the resulting read_buf contains both unused bytes and
727 // bytes ejected by chunked-encoding filter.
728 TEST(HttpStreamParser
, ReceivedBytesChunkedTransferExcludesNextResponse
) {
729 std::string response
= "HTTP/1.1 200 OK\r\n"
730 "Transfer-Encoding: chunked\r\n\r\n"
735 std::string next_response
= "foo bar\r\n";
736 std::string data
= response
+ next_response
;
738 SimpleGetRunner get_runner
;
739 get_runner
.AddInitialData(data
);
740 get_runner
.SetupParserAndSendRequest();
741 get_runner
.ReadHeaders();
742 int read_lengths
[] = {4, 3, 6, 2, 6, 0};
743 get_runner
.ReadBody(7, read_lengths
);
744 int64 response_size
= response
.size();
745 EXPECT_EQ(response_size
, get_runner
.parser()->received_bytes());
746 int64 next_response_size
= next_response
.size();
747 EXPECT_EQ(next_response_size
, get_runner
.read_buffer()->offset());
750 // Test that data transfered in multiple reads is correctly processed.
751 // We feed data into 4-bytes reads. Also we set length of read
752 // buffer to 5-bytes to test all possible buffer misaligments.
753 TEST(HttpStreamParser
, ReceivedBytesMultipleReads
) {
754 std::string headers
= "HTTP/1.1 200 OK\r\n"
755 "Content-Length: 33\r\n\r\n";
756 std::string body
= "foo bar baz\r\n"
757 "sputnik mir babushka";
758 std::string response
= headers
+ body
;
760 size_t receive_length
= 4;
761 std::vector
<std::string
> blocks
;
762 for (size_t i
= 0; i
< response
.size(); i
+= receive_length
) {
763 size_t length
= std::min(receive_length
, response
.size() - i
);
764 blocks
.push_back(response
.substr(i
, length
));
767 SimpleGetRunner get_runner
;
768 for (std::vector
<std::string
>::size_type i
= 0; i
< blocks
.size(); ++i
)
769 get_runner
.AddRead(blocks
[i
]);
770 get_runner
.SetupParserAndSendRequest();
771 get_runner
.ReadHeaders();
772 int64 headers_size
= headers
.size();
773 EXPECT_EQ(headers_size
, get_runner
.parser()->received_bytes());
774 int read_lengths
[] = {1, 4, 4, 4, 4, 4, 4, 4, 4, 0};
775 get_runner
.ReadBody(receive_length
+ 1, read_lengths
);
776 int64 response_size
= response
.size();
777 EXPECT_EQ(response_size
, get_runner
.parser()->received_bytes());
780 // Test that "continue" HTTP header is counted as "received_bytes".
781 TEST(HttpStreamParser
, ReceivedBytesIncludesContinueHeader
) {
782 std::string status100
= "HTTP/1.1 100 OK\r\n\r\n";
783 std::string headers
= "HTTP/1.1 200 OK\r\n"
784 "Content-Length: 7\r\n\r\n";
785 int64 headers_size
= status100
.size() + headers
.size();
786 std::string body
= "content";
787 std::string response
= headers
+ body
;
789 SimpleGetRunner get_runner
;
790 get_runner
.AddRead(status100
);
791 get_runner
.AddRead(response
);
792 get_runner
.SetupParserAndSendRequest();
793 get_runner
.ReadHeaders();
794 EXPECT_EQ(100, get_runner
.response_info()->headers
->response_code());
795 int64 status100_size
= status100
.size();
796 EXPECT_EQ(status100_size
, get_runner
.parser()->received_bytes());
797 get_runner
.ReadHeaders();
798 EXPECT_EQ(200, get_runner
.response_info()->headers
->response_code());
799 EXPECT_EQ(headers_size
, get_runner
.parser()->received_bytes());
800 int64 response_size
= headers_size
+ body
.size();
801 int body_size
= body
.size();
802 int read_lengths
[] = {body_size
, 0};
803 get_runner
.ReadBody(body_size
, read_lengths
);
804 EXPECT_EQ(response_size
, get_runner
.parser()->received_bytes());
807 // Test that an HttpStreamParser can be read from after it's received headers
808 // and data structures owned by its owner have been deleted. This happens
809 // when a ResponseBodyDrainer is used.
810 TEST(HttpStreamParser
, ReadAfterUnownedObjectsDestroyed
) {
811 MockWrite writes
[] = {
812 MockWrite(SYNCHRONOUS
, 0,
813 "GET /foo.html HTTP/1.1\r\n\r\n"),
814 MockWrite(SYNCHRONOUS
, 1, "1"),
817 const int kBodySize
= 1;
819 MockRead(SYNCHRONOUS
, 5, "HTTP/1.1 200 OK\r\n"),
820 MockRead(SYNCHRONOUS
, 6, "Content-Length: 1\r\n\r\n"),
821 MockRead(SYNCHRONOUS
, 6, "Connection: Keep-Alive\r\n\r\n"),
822 MockRead(SYNCHRONOUS
, 7, "1"),
823 MockRead(SYNCHRONOUS
, 0, 8), // EOF
826 StaticSocketDataProvider
data(reads
, arraysize(reads
), writes
,
828 data
.set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
830 scoped_ptr
<MockTCPClientSocket
> transport(
831 new MockTCPClientSocket(AddressList(), NULL
, &data
));
833 TestCompletionCallback callback
;
834 ASSERT_EQ(OK
, transport
->Connect(callback
.callback()));
836 scoped_ptr
<ClientSocketHandle
> socket_handle(new ClientSocketHandle
);
837 socket_handle
->SetSocket(transport
.PassAs
<StreamSocket
>());
839 scoped_ptr
<HttpRequestInfo
> request_info(new HttpRequestInfo());
840 request_info
->method
= "GET";
841 request_info
->url
= GURL("http://somewhere/foo.html");
843 scoped_refptr
<GrowableIOBuffer
> read_buffer(new GrowableIOBuffer
);
844 HttpStreamParser
parser(socket_handle
.get(), request_info
.get(),
845 read_buffer
.get(), BoundNetLog());
847 scoped_ptr
<HttpRequestHeaders
> request_headers(new HttpRequestHeaders());
848 scoped_ptr
<HttpResponseInfo
> response_info(new HttpResponseInfo());
849 ASSERT_EQ(OK
, parser
.SendRequest("GET /foo.html HTTP/1.1\r\n",
850 *request_headers
, response_info
.get(), callback
.callback()));
851 ASSERT_EQ(OK
, parser
.ReadResponseHeaders(callback
.callback()));
853 // If the object that owns the HttpStreamParser is deleted, it takes the
854 // objects passed to the HttpStreamParser with it.
855 request_info
.reset();
856 request_headers
.reset();
857 response_info
.reset();
859 scoped_refptr
<IOBuffer
> body_buffer(new IOBuffer(kBodySize
));
860 ASSERT_EQ(kBodySize
, parser
.ReadResponseBody(
861 body_buffer
.get(), kBodySize
, callback
.callback()));