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(
559 buffer
.get(), user_buf_len
, callback
.callback());
560 EXPECT_EQ(read_lengths
[i
], rv
);
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_
;
581 // Test that HTTP/0.9 response size is correctly calculated.
582 TEST(HttpStreamParser
, ReceivedBytesNoHeaders
) {
583 std::string response
= "hello\r\nworld\r\n";
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());
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
;
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());
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
;
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());
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
;
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());
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
;
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());
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
;
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());
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"
736 std::string next_response
= "foo bar\r\n";
737 std::string data
= response
+ next_response
;
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());
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
;
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
));
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());
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
;
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());
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"),
818 const int kBodySize
= 1;
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
827 StaticSocketDataProvider
data(reads
, arraysize(reads
), writes
,
829 data
.set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
831 scoped_ptr
<MockTCPClientSocket
> transport(
832 new MockTCPClientSocket(AddressList(), NULL
, &data
));
834 TestCompletionCallback callback
;
835 ASSERT_EQ(OK
, transport
->Connect(callback
.callback()));
837 scoped_ptr
<ClientSocketHandle
> socket_handle(new ClientSocketHandle
);
838 socket_handle
->SetSocket(transport
.PassAs
<StreamSocket
>());
840 scoped_ptr
<HttpRequestInfo
> request_info(new HttpRequestInfo());
841 request_info
->method
= "GET";
842 request_info
->url
= GURL("http://somewhere/foo.html");
844 scoped_refptr
<GrowableIOBuffer
> read_buffer(new GrowableIOBuffer
);
845 HttpStreamParser
parser(socket_handle
.get(), request_info
.get(),
846 read_buffer
.get(), BoundNetLog());
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()));
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();
860 scoped_refptr
<IOBuffer
> body_buffer(new IOBuffer(kBodySize
));
861 ASSERT_EQ(kBodySize
, parser
.ReadResponseBody(
862 body_buffer
.get(), kBodySize
, callback
.callback()));