blob: b431549fe83337713fc9f6c743cd97636a2e36d7 [file] [log] [blame]
[email protected]6db833d12012-01-21 00:45:191// 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
sclittlebe1ccf62015-09-02 19:40:367#include <stdint.h>
8
[email protected]3cb5fc22013-12-12 19:40:569#include <algorithm>
10#include <string>
11#include <vector>
12
[email protected]57999812013-02-24 05:40:5213#include "base/files/file_path.h"
thestigd8df0332014-09-04 06:33:2914#include "base/files/file_util.h"
[email protected]ea1a3f62012-11-16 20:34:2315#include "base/files/scoped_temp_dir.h"
[email protected]4750937f2012-06-15 20:44:2116#include "base/memory/ref_counted.h"
[email protected]0f972882013-09-20 04:34:2017#include "base/run_loop.h"
[email protected]d069c11a2013-04-13 00:01:5518#include "base/strings/string_piece.h"
[email protected]125ef482013-06-11 18:32:4719#include "base/strings/stringprintf.h"
skyostil4891b25b2015-06-11 11:43:4520#include "base/thread_task_runner_handle.h"
mmenkecbc2b712014-10-09 20:29:0721#include "net/base/chunked_upload_data_stream.h"
22#include "net/base/elements_upload_data_stream.h"
[email protected]4750937f2012-06-15 20:44:2123#include "net/base/io_buffer.h"
[email protected]6db833d12012-01-21 00:45:1924#include "net/base/net_errors.h"
[email protected]4750937f2012-06-15 20:44:2125#include "net/base/test_completion_callback.h"
[email protected]b2d26cfd2012-12-11 10:36:0626#include "net/base/upload_bytes_element_reader.h"
[email protected]b2d26cfd2012-12-11 10:36:0627#include "net/base/upload_file_element_reader.h"
[email protected]4750937f2012-06-15 20:44:2128#include "net/http/http_request_headers.h"
29#include "net/http/http_request_info.h"
[email protected]df8e6382013-11-07 00:19:0630#include "net/http/http_response_headers.h"
[email protected]4750937f2012-06-15 20:44:2131#include "net/http/http_response_info.h"
32#include "net/socket/client_socket_handle.h"
33#include "net/socket/socket_test_util.h"
[email protected]6db833d12012-01-21 00:45:1934#include "testing/gtest/include/gtest/gtest.h"
[email protected]f89276a72013-07-12 06:41:5435#include "url/gurl.h"
[email protected]6db833d12012-01-21 00:45:1936
37namespace net {
38
[email protected]390489b2013-12-09 10:49:0139namespace {
40
[email protected]6db833d12012-01-21 00:45:1941const size_t kOutputSize = 1024; // Just large enough for this test.
42// The number of bytes that can fit in a buffer of kOutputSize.
43const size_t kMaxPayloadSize =
44 kOutputSize - HttpStreamParser::kChunkHeaderFooterSize;
45
mmenke3dc8c88b2015-02-19 15:11:2746// Helper method to create a connected ClientSocketHandle using |data|.
47// Modifies |data|.
48scoped_ptr<ClientSocketHandle> CreateConnectedSocketHandle(
mmenkef22be0ee2015-06-05 15:39:0249 SequencedSocketData* data) {
mmenke3dc8c88b2015-02-19 15:11:2750 data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
51
mmenkef22be0ee2015-06-05 15:39:0252 scoped_ptr<MockTCPClientSocket> socket(
53 new MockTCPClientSocket(net::AddressList(), nullptr, data));
54 data->set_socket(socket.get());
mmenke3dc8c88b2015-02-19 15:11:2755
56 TestCompletionCallback callback;
mmenkef22be0ee2015-06-05 15:39:0257 EXPECT_EQ(OK, socket->Connect(callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:2758
59 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
mmenkef22be0ee2015-06-05 15:39:0260 socket_handle->SetSocket(socket.Pass());
mmenke3dc8c88b2015-02-19 15:11:2761 return socket_handle.Pass();
62}
63
[email protected]6db833d12012-01-21 00:45:1964// The empty payload is how the last chunk is encoded.
65TEST(HttpStreamParser, EncodeChunk_EmptyPayload) {
66 char output[kOutputSize];
67
68 const base::StringPiece kPayload = "";
69 const base::StringPiece kExpected = "0\r\n\r\n";
70 const int num_bytes_written =
71 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
72 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
73 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
74}
75
76TEST(HttpStreamParser, EncodeChunk_ShortPayload) {
77 char output[kOutputSize];
78
79 const std::string kPayload("foo\x00\x11\x22", 6);
80 // 11 = payload size + sizeof("6") + CRLF x 2.
81 const std::string kExpected("6\r\nfoo\x00\x11\x22\r\n", 11);
82 const int num_bytes_written =
83 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
84 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
85 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
86}
87
88TEST(HttpStreamParser, EncodeChunk_LargePayload) {
89 char output[kOutputSize];
90
91 const std::string kPayload(1000, '\xff'); // '\xff' x 1000.
92 // 3E8 = 1000 in hex.
93 const std::string kExpected = "3E8\r\n" + kPayload + "\r\n";
94 const int num_bytes_written =
95 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
96 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
97 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
98}
99
100TEST(HttpStreamParser, EncodeChunk_FullPayload) {
101 char output[kOutputSize];
102
103 const std::string kPayload(kMaxPayloadSize, '\xff');
104 // 3F4 = 1012 in hex.
105 const std::string kExpected = "3F4\r\n" + kPayload + "\r\n";
106 const int num_bytes_written =
107 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
108 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
109 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
110}
111
112TEST(HttpStreamParser, EncodeChunk_TooLargePayload) {
113 char output[kOutputSize];
114
115 // The payload is one byte larger the output buffer size.
116 const std::string kPayload(kMaxPayloadSize + 1, '\xff');
117 const int num_bytes_written =
118 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
119 ASSERT_EQ(ERR_INVALID_ARGUMENT, num_bytes_written);
120}
121
[email protected]75577ec2012-01-24 23:41:50122TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_NoBody) {
[email protected]7a1fcff2012-01-24 01:07:49123 // Shouldn't be merged if upload data is non-existent.
[email protected]75577ec2012-01-24 23:41:50124 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
125 "some header", NULL));
[email protected]7a1fcff2012-01-24 01:07:49126}
127
[email protected]75577ec2012-01-24 23:41:50128TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_EmptyBody) {
[email protected]b2d26cfd2012-12-11 10:36:06129 ScopedVector<UploadElementReader> element_readers;
[email protected]96c77a72013-09-24 09:49:20130 scoped_ptr<UploadDataStream> body(
mmenkecbc2b712014-10-09 20:29:07131 new ElementsUploadDataStream(element_readers.Pass(), 0));
[email protected]4db27d82012-12-20 11:50:24132 ASSERT_EQ(OK, body->Init(CompletionCallback()));
[email protected]7a1fcff2012-01-24 01:07:49133 // Shouldn't be merged if upload data is empty.
[email protected]75577ec2012-01-24 23:41:50134 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
135 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49136}
137
[email protected]75577ec2012-01-24 23:41:50138TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_ChunkedBody) {
[email protected]7a1fcff2012-01-24 01:07:49139 const std::string payload = "123";
mmenkecbc2b712014-10-09 20:29:07140 scoped_ptr<ChunkedUploadDataStream> body(new ChunkedUploadDataStream(0));
141 body->AppendData(payload.data(), payload.size(), true);
142 ASSERT_EQ(OK, body->Init(TestCompletionCallback().callback()));
[email protected]7a1fcff2012-01-24 01:07:49143 // Shouldn't be merged if upload data carries chunked data.
[email protected]75577ec2012-01-24 23:41:50144 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
145 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49146}
147
[email protected]75577ec2012-01-24 23:41:50148TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_FileBody) {
wkorman30fe3fd2015-11-06 23:43:42149 // Create an empty temporary file.
150 base::ScopedTempDir temp_dir;
151 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
152 base::FilePath temp_file_path;
153 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &temp_file_path));
154
[email protected]0f972882013-09-20 04:34:20155 {
156 ScopedVector<UploadElementReader> element_readers;
[email protected]7a1fcff2012-01-24 01:07:49157
[email protected]0f972882013-09-20 04:34:20158 element_readers.push_back(
skyostil4891b25b2015-06-11 11:43:45159 new UploadFileElementReader(base::ThreadTaskRunnerHandle::Get().get(),
160 temp_file_path, 0, 0, base::Time()));
[email protected]7a1fcff2012-01-24 01:07:49161
[email protected]0f972882013-09-20 04:34:20162 scoped_ptr<UploadDataStream> body(
mmenkecbc2b712014-10-09 20:29:07163 new ElementsUploadDataStream(element_readers.Pass(), 0));
[email protected]0f972882013-09-20 04:34:20164 TestCompletionCallback callback;
165 ASSERT_EQ(ERR_IO_PENDING, body->Init(callback.callback()));
166 ASSERT_EQ(OK, callback.WaitForResult());
167 // Shouldn't be merged if upload data carries a file, as it's not in-memory.
168 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
169 "some header", body.get()));
170 }
wkorman30fe3fd2015-11-06 23:43:42171
[email protected]0f972882013-09-20 04:34:20172 // UploadFileElementReaders may post clean-up tasks on destruction.
173 base::RunLoop().RunUntilIdle();
[email protected]7a1fcff2012-01-24 01:07:49174}
175
[email protected]75577ec2012-01-24 23:41:50176TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_SmallBodyInMemory) {
[email protected]b2d26cfd2012-12-11 10:36:06177 ScopedVector<UploadElementReader> element_readers;
[email protected]7a1fcff2012-01-24 01:07:49178 const std::string payload = "123";
[email protected]b2d26cfd2012-12-11 10:36:06179 element_readers.push_back(new UploadBytesElementReader(
180 payload.data(), payload.size()));
[email protected]7a1fcff2012-01-24 01:07:49181
[email protected]96c77a72013-09-24 09:49:20182 scoped_ptr<UploadDataStream> body(
mmenkecbc2b712014-10-09 20:29:07183 new ElementsUploadDataStream(element_readers.Pass(), 0));
[email protected]4db27d82012-12-20 11:50:24184 ASSERT_EQ(OK, body->Init(CompletionCallback()));
[email protected]7a1fcff2012-01-24 01:07:49185 // Yes, should be merged if the in-memory body is small here.
[email protected]75577ec2012-01-24 23:41:50186 ASSERT_TRUE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
187 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49188}
189
[email protected]75577ec2012-01-24 23:41:50190TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_LargeBodyInMemory) {
[email protected]b2d26cfd2012-12-11 10:36:06191 ScopedVector<UploadElementReader> element_readers;
[email protected]7a1fcff2012-01-24 01:07:49192 const std::string payload(10000, 'a'); // 'a' x 10000.
[email protected]b2d26cfd2012-12-11 10:36:06193 element_readers.push_back(new UploadBytesElementReader(
194 payload.data(), payload.size()));
[email protected]7a1fcff2012-01-24 01:07:49195
[email protected]96c77a72013-09-24 09:49:20196 scoped_ptr<UploadDataStream> body(
mmenkecbc2b712014-10-09 20:29:07197 new ElementsUploadDataStream(element_readers.Pass(), 0));
[email protected]4db27d82012-12-20 11:50:24198 ASSERT_EQ(OK, body->Init(CompletionCallback()));
[email protected]7a1fcff2012-01-24 01:07:49199 // Shouldn't be merged if the in-memory body is large here.
[email protected]75577ec2012-01-24 23:41:50200 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
201 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49202}
203
sclittlebe1ccf62015-09-02 19:40:36204TEST(HttpStreamParser, SentBytesNoHeaders) {
205 MockWrite writes[] = {
206 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n\r\n"),
207 };
208
209 SequencedSocketData data(nullptr, 0, writes, arraysize(writes));
210 scoped_ptr<ClientSocketHandle> socket_handle =
211 CreateConnectedSocketHandle(&data);
212
213 HttpRequestInfo request;
214 request.method = "GET";
215 request.url = GURL("http://localhost");
216
217 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
218 HttpStreamParser parser(socket_handle.get(), &request, read_buffer.get(),
219 BoundNetLog());
220
221 HttpResponseInfo response;
222 TestCompletionCallback callback;
223 EXPECT_EQ(OK, parser.SendRequest("GET / HTTP/1.1\r\n", HttpRequestHeaders(),
224 &response, callback.callback()));
225
226 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
227}
228
229TEST(HttpStreamParser, SentBytesWithHeaders) {
230 MockWrite writes[] = {
231 MockWrite(SYNCHRONOUS, 0,
232 "GET / HTTP/1.1\r\n"
233 "Host: localhost\r\n"
234 "Connection: Keep-Alive\r\n\r\n"),
235 };
236
237 SequencedSocketData data(nullptr, 0, writes, arraysize(writes));
238 scoped_ptr<ClientSocketHandle> socket_handle =
239 CreateConnectedSocketHandle(&data);
240
241 HttpRequestInfo request;
242 request.method = "GET";
243 request.url = GURL("http://localhost");
244
245 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
246 HttpStreamParser parser(socket_handle.get(), &request, read_buffer.get(),
247 BoundNetLog());
248
249 HttpRequestHeaders headers;
250 headers.SetHeader("Host", "localhost");
251 headers.SetHeader("Connection", "Keep-Alive");
252
253 HttpResponseInfo response;
254 TestCompletionCallback callback;
255 EXPECT_EQ(OK, parser.SendRequest("GET / HTTP/1.1\r\n", headers, &response,
256 callback.callback()));
257
258 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
259}
260
261TEST(HttpStreamParser, SentBytesWithHeadersMultiWrite) {
262 MockWrite writes[] = {
263 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n"),
264 MockWrite(SYNCHRONOUS, 1, "Host: localhost\r\n"),
265 MockWrite(SYNCHRONOUS, 2, "Connection: Keep-Alive\r\n\r\n"),
266 };
267
268 SequencedSocketData data(nullptr, 0, writes, arraysize(writes));
269 scoped_ptr<ClientSocketHandle> socket_handle =
270 CreateConnectedSocketHandle(&data);
271
272 HttpRequestInfo request;
273 request.method = "GET";
274 request.url = GURL("http://localhost");
275
276 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
277 HttpStreamParser parser(socket_handle.get(), &request, read_buffer.get(),
278 BoundNetLog());
279
280 HttpRequestHeaders headers;
281 headers.SetHeader("Host", "localhost");
282 headers.SetHeader("Connection", "Keep-Alive");
283
284 HttpResponseInfo response;
285 TestCompletionCallback callback;
286
287 EXPECT_EQ(OK, parser.SendRequest("GET / HTTP/1.1\r\n", headers, &response,
288 callback.callback()));
289
290 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
291}
292
293TEST(HttpStreamParser, SentBytesWithErrorWritingHeaders) {
294 MockWrite writes[] = {
295 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n"),
296 MockWrite(SYNCHRONOUS, 1, "Host: localhost\r\n"),
297 MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET, 2),
298 };
299
300 SequencedSocketData data(nullptr, 0, writes, arraysize(writes));
301 scoped_ptr<ClientSocketHandle> socket_handle =
302 CreateConnectedSocketHandle(&data);
303
304 HttpRequestInfo request;
305 request.method = "GET";
306 request.url = GURL("http://localhost");
307
308 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
309 HttpStreamParser parser(socket_handle.get(), &request, read_buffer.get(),
310 BoundNetLog());
311
312 HttpRequestHeaders headers;
313 headers.SetHeader("Host", "localhost");
314 headers.SetHeader("Connection", "Keep-Alive");
315
316 HttpResponseInfo response;
317 TestCompletionCallback callback;
318 EXPECT_EQ(ERR_CONNECTION_RESET,
319 parser.SendRequest("GET / HTTP/1.1\r\n", headers, &response,
320 callback.callback()));
321
322 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
323}
324
325TEST(HttpStreamParser, SentBytesPost) {
326 MockWrite writes[] = {
327 MockWrite(SYNCHRONOUS, 0, "POST / HTTP/1.1\r\n"),
328 MockWrite(SYNCHRONOUS, 1, "Content-Length: 12\r\n\r\n"),
329 MockWrite(SYNCHRONOUS, 2, "hello world!"),
330 };
331
332 SequencedSocketData data(nullptr, 0, writes, arraysize(writes));
333 scoped_ptr<ClientSocketHandle> socket_handle =
334 CreateConnectedSocketHandle(&data);
335
336 ScopedVector<UploadElementReader> element_readers;
337 element_readers.push_back(new UploadBytesElementReader("hello world!", 12));
338 ElementsUploadDataStream upload_data_stream(element_readers.Pass(), 0);
339 ASSERT_EQ(OK, upload_data_stream.Init(TestCompletionCallback().callback()));
340
341 HttpRequestInfo request;
342 request.method = "POST";
343 request.url = GURL("http://localhost");
344 request.upload_data_stream = &upload_data_stream;
345
346 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
347 HttpStreamParser parser(socket_handle.get(), &request, read_buffer.get(),
348 BoundNetLog());
349
350 HttpRequestHeaders headers;
351 headers.SetHeader("Content-Length", "12");
352
353 HttpResponseInfo response;
354 TestCompletionCallback callback;
355 EXPECT_EQ(OK, parser.SendRequest("POST / HTTP/1.1\r\n", headers, &response,
356 callback.callback()));
357
358 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
359}
360
361TEST(HttpStreamParser, SentBytesChunkedPostError) {
362 static const char kChunk[] = "Chunk 1";
363
364 MockWrite writes[] = {
365 MockWrite(ASYNC, 0, "POST / HTTP/1.1\r\n"),
366 MockWrite(ASYNC, 1, "Transfer-Encoding: chunked\r\n\r\n"),
367 MockWrite(ASYNC, 2, "7\r\nChunk 1\r\n"),
368 MockWrite(SYNCHRONOUS, ERR_FAILED, 3),
369 };
370
371 SequencedSocketData data(nullptr, 0, writes, arraysize(writes));
372 scoped_ptr<ClientSocketHandle> socket_handle =
373 CreateConnectedSocketHandle(&data);
374
375 ChunkedUploadDataStream upload_data_stream(0);
376 ASSERT_EQ(OK, upload_data_stream.Init(TestCompletionCallback().callback()));
377
378 HttpRequestInfo request;
379 request.method = "POST";
380 request.url = GURL("http://localhost");
381 request.upload_data_stream = &upload_data_stream;
382
383 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
384 HttpStreamParser parser(socket_handle.get(), &request, read_buffer.get(),
385 BoundNetLog());
386
387 HttpRequestHeaders headers;
388 headers.SetHeader("Transfer-Encoding", "chunked");
389
390 HttpResponseInfo response;
391 TestCompletionCallback callback;
392 EXPECT_EQ(ERR_IO_PENDING, parser.SendRequest("POST / HTTP/1.1\r\n", headers,
393 &response, callback.callback()));
394
395 base::RunLoop().RunUntilIdle();
396 upload_data_stream.AppendData(kChunk, arraysize(kChunk) - 1, false);
397
398 base::RunLoop().RunUntilIdle();
399 // This write should fail.
400 upload_data_stream.AppendData(kChunk, arraysize(kChunk) - 1, false);
401 EXPECT_EQ(ERR_FAILED, callback.WaitForResult());
402
403 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
404}
405
[email protected]4750937f2012-06-15 20:44:21406// Test to ensure the HttpStreamParser state machine does not get confused
mmenke3dc8c88b2015-02-19 15:11:27407// when sending a request with a chunked body with only one chunk that becomes
408// available asynchronously.
409TEST(HttpStreamParser, AsyncSingleChunkAndAsyncSocket) {
410 static const char kChunk[] = "Chunk";
411
412 MockWrite writes[] = {
413 MockWrite(ASYNC, 0,
414 "GET /one.html HTTP/1.1\r\n"
415 "Transfer-Encoding: chunked\r\n\r\n"),
416 MockWrite(ASYNC, 1, "5\r\nChunk\r\n"),
417 MockWrite(ASYNC, 2, "0\r\n\r\n"),
418 };
419
420 // The size of the response body, as reflected in the Content-Length of the
421 // MockRead below.
422 static const int kBodySize = 8;
423
424 MockRead reads[] = {
425 MockRead(ASYNC, 3, "HTTP/1.1 200 OK\r\n"),
426 MockRead(ASYNC, 4, "Content-Length: 8\r\n\r\n"),
427 MockRead(ASYNC, 5, "one.html"),
428 MockRead(SYNCHRONOUS, 0, 6), // EOF
429 };
430
431 ChunkedUploadDataStream upload_stream(0);
432 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
433
mmenkef22be0ee2015-06-05 15:39:02434 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
mmenke3dc8c88b2015-02-19 15:11:27435 scoped_ptr<ClientSocketHandle> socket_handle =
436 CreateConnectedSocketHandle(&data);
437
438 HttpRequestInfo request_info;
439 request_info.method = "GET";
440 request_info.url = GURL("http://localhost");
441 request_info.upload_data_stream = &upload_stream;
442
443 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
444 HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
445 BoundNetLog());
446
447 HttpRequestHeaders request_headers;
448 request_headers.SetHeader("Transfer-Encoding", "chunked");
449
450 HttpResponseInfo response_info;
451 TestCompletionCallback callback;
452 // This will attempt to Write() the initial request and headers, which will
453 // complete asynchronously.
454 ASSERT_EQ(ERR_IO_PENDING,
455 parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
456 &response_info, callback.callback()));
457
mmenkef22be0ee2015-06-05 15:39:02458 // Complete the initial request write. Callback should not have been invoked.
459 base::RunLoop().RunUntilIdle();
mmenke3dc8c88b2015-02-19 15:11:27460 ASSERT_FALSE(callback.have_result());
461
mmenkef22be0ee2015-06-05 15:39:02462 // Now append the only chunk and wait for the callback.
mmenke3dc8c88b2015-02-19 15:11:27463 upload_stream.AppendData(kChunk, arraysize(kChunk) - 1, true);
mmenke3dc8c88b2015-02-19 15:11:27464 ASSERT_EQ(OK, callback.WaitForResult());
465
466 // Attempt to read the response status and the response headers.
467 ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:27468 ASSERT_GT(callback.WaitForResult(), 0);
469
470 // Finally, attempt to read the response body.
471 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
472 ASSERT_EQ(ERR_IO_PENDING,
473 parser.ReadResponseBody(body_buffer.get(), kBodySize,
474 callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:27475 ASSERT_EQ(kBodySize, callback.WaitForResult());
sclittlebe1ccf62015-09-02 19:40:36476
477 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
478 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), parser.received_bytes());
mmenke3dc8c88b2015-02-19 15:11:27479}
480
481// Test to ensure the HttpStreamParser state machine does not get confused
482// when sending a request with a chunked body with only one chunk that is
483// available synchronously.
484TEST(HttpStreamParser, SyncSingleChunkAndAsyncSocket) {
485 static const char kChunk[] = "Chunk";
486
487 MockWrite writes[] = {
488 MockWrite(ASYNC, 0,
489 "GET /one.html HTTP/1.1\r\n"
490 "Transfer-Encoding: chunked\r\n\r\n"),
491 MockWrite(ASYNC, 1, "5\r\nChunk\r\n"),
492 MockWrite(ASYNC, 2, "0\r\n\r\n"),
493 };
494
495 // The size of the response body, as reflected in the Content-Length of the
496 // MockRead below.
497 static const int kBodySize = 8;
498
499 MockRead reads[] = {
500 MockRead(ASYNC, 3, "HTTP/1.1 200 OK\r\n"),
501 MockRead(ASYNC, 4, "Content-Length: 8\r\n\r\n"),
502 MockRead(ASYNC, 5, "one.html"),
503 MockRead(SYNCHRONOUS, 0, 6), // EOF
504 };
505
506 ChunkedUploadDataStream upload_stream(0);
507 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
508 // Append the only chunk.
509 upload_stream.AppendData(kChunk, arraysize(kChunk) - 1, true);
510
mmenkef22be0ee2015-06-05 15:39:02511 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
mmenke3dc8c88b2015-02-19 15:11:27512 scoped_ptr<ClientSocketHandle> socket_handle =
513 CreateConnectedSocketHandle(&data);
514
515 HttpRequestInfo request_info;
516 request_info.method = "GET";
517 request_info.url = GURL("http://localhost");
518 request_info.upload_data_stream = &upload_stream;
519
520 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
521 HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
522 BoundNetLog());
523
524 HttpRequestHeaders request_headers;
525 request_headers.SetHeader("Transfer-Encoding", "chunked");
526
527 HttpResponseInfo response_info;
528 TestCompletionCallback callback;
529 // This will attempt to Write() the initial request and headers, which will
530 // complete asynchronously.
531 ASSERT_EQ(ERR_IO_PENDING,
532 parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
533 &response_info, callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:27534 ASSERT_EQ(OK, callback.WaitForResult());
535
536 // Attempt to read the response status and the response headers.
537 ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:27538 ASSERT_GT(callback.WaitForResult(), 0);
539
540 // Finally, attempt to read the response body.
541 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
542 ASSERT_EQ(ERR_IO_PENDING,
543 parser.ReadResponseBody(body_buffer.get(), kBodySize,
544 callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:27545 ASSERT_EQ(kBodySize, callback.WaitForResult());
sclittlebe1ccf62015-09-02 19:40:36546
547 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
548 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), parser.received_bytes());
mmenke3dc8c88b2015-02-19 15:11:27549}
550
551// Test to ensure the HttpStreamParser state machine does not get confused
[email protected]4750937f2012-06-15 20:44:21552// when sending a request with a chunked body, where chunks become available
553// asynchronously, over a socket where writes may also complete
554// asynchronously.
555// This is a regression test for http://crbug.com/132243
mmenke3dc8c88b2015-02-19 15:11:27556TEST(HttpStreamParser, AsyncChunkAndAsyncSocketWithMultipleChunks) {
[email protected]4750937f2012-06-15 20:44:21557 // The chunks that will be written in the request, as reflected in the
558 // MockWrites below.
559 static const char kChunk1[] = "Chunk 1";
560 static const char kChunk2[] = "Chunky 2";
561 static const char kChunk3[] = "Test 3";
562
563 MockWrite writes[] = {
mmenke3dc8c88b2015-02-19 15:11:27564 MockWrite(ASYNC, 0,
565 "GET /one.html HTTP/1.1\r\n"
566 "Transfer-Encoding: chunked\r\n\r\n"),
567 MockWrite(ASYNC, 1, "7\r\nChunk 1\r\n"),
568 MockWrite(ASYNC, 2, "8\r\nChunky 2\r\n"),
569 MockWrite(ASYNC, 3, "6\r\nTest 3\r\n"),
570 MockWrite(ASYNC, 4, "0\r\n\r\n"),
[email protected]4750937f2012-06-15 20:44:21571 };
572
573 // The size of the response body, as reflected in the Content-Length of the
574 // MockRead below.
575 static const int kBodySize = 8;
576
577 MockRead reads[] = {
578 MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"),
579 MockRead(ASYNC, 6, "Content-Length: 8\r\n\r\n"),
580 MockRead(ASYNC, 7, "one.html"),
[email protected]d55b30a2012-07-21 00:35:39581 MockRead(SYNCHRONOUS, 0, 8), // EOF
[email protected]4750937f2012-06-15 20:44:21582 };
583
mmenkecbc2b712014-10-09 20:29:07584 ChunkedUploadDataStream upload_stream(0);
585 upload_stream.AppendData(kChunk1, arraysize(kChunk1) - 1, false);
586 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
[email protected]9a963892012-11-01 11:48:13587
mmenkef22be0ee2015-06-05 15:39:02588 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
mmenke3dc8c88b2015-02-19 15:11:27589 scoped_ptr<ClientSocketHandle> socket_handle =
590 CreateConnectedSocketHandle(&data);
[email protected]4750937f2012-06-15 20:44:21591
592 HttpRequestInfo request_info;
593 request_info.method = "GET";
594 request_info.url = GURL("http://localhost");
[email protected]bf3eb002012-11-15 05:50:11595 request_info.upload_data_stream = &upload_stream;
[email protected]4750937f2012-06-15 20:44:21596
597 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
[email protected]90499482013-06-01 00:39:50598 HttpStreamParser parser(
599 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog());
[email protected]4750937f2012-06-15 20:44:21600
[email protected]4750937f2012-06-15 20:44:21601 HttpRequestHeaders request_headers;
[email protected]4750937f2012-06-15 20:44:21602 request_headers.SetHeader("Transfer-Encoding", "chunked");
[email protected]4750937f2012-06-15 20:44:21603
604 HttpResponseInfo response_info;
mmenke3dc8c88b2015-02-19 15:11:27605 TestCompletionCallback callback;
[email protected]4750937f2012-06-15 20:44:21606 // This will attempt to Write() the initial request and headers, which will
607 // complete asynchronously.
mmenke3dc8c88b2015-02-19 15:11:27608 ASSERT_EQ(ERR_IO_PENDING,
609 parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
610 &response_info, callback.callback()));
[email protected]4750937f2012-06-15 20:44:21611 ASSERT_FALSE(callback.have_result());
612
mmenkef22be0ee2015-06-05 15:39:02613 // Sending the request and the first chunk completes.
614 base::RunLoop().RunUntilIdle();
615 ASSERT_FALSE(callback.have_result());
616
617 // Now append another chunk.
mmenkecbc2b712014-10-09 20:29:07618 upload_stream.AppendData(kChunk2, arraysize(kChunk2) - 1, false);
[email protected]4750937f2012-06-15 20:44:21619 ASSERT_FALSE(callback.have_result());
620
mmenkef22be0ee2015-06-05 15:39:02621 // Add the final chunk, while the write for the second is still pending,
622 // which should not confuse the state machine.
mmenkecbc2b712014-10-09 20:29:07623 upload_stream.AppendData(kChunk3, arraysize(kChunk3) - 1, true);
[email protected]4750937f2012-06-15 20:44:21624 ASSERT_FALSE(callback.have_result());
625
mmenkef22be0ee2015-06-05 15:39:02626 // Wait for writes to complete.
mmenke3dc8c88b2015-02-19 15:11:27627 ASSERT_EQ(OK, callback.WaitForResult());
[email protected]4750937f2012-06-15 20:44:21628
629 // Attempt to read the response status and the response headers.
mmenke3dc8c88b2015-02-19 15:11:27630 ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:27631 ASSERT_GT(callback.WaitForResult(), 0);
[email protected]4750937f2012-06-15 20:44:21632
633 // Finally, attempt to read the response body.
634 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
mmenke3dc8c88b2015-02-19 15:11:27635 ASSERT_EQ(ERR_IO_PENDING,
636 parser.ReadResponseBody(body_buffer.get(), kBodySize,
637 callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:27638 ASSERT_EQ(kBodySize, callback.WaitForResult());
sclittlebe1ccf62015-09-02 19:40:36639
640 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
641 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), parser.received_bytes());
mmenke3dc8c88b2015-02-19 15:11:27642}
643
644// Test to ensure the HttpStreamParser state machine does not get confused
645// when there's only one "chunk" with 0 bytes, and is received from the
646// UploadStream only after sending the request headers successfully.
647TEST(HttpStreamParser, AsyncEmptyChunkedUpload) {
648 MockWrite writes[] = {
649 MockWrite(ASYNC, 0,
650 "GET /one.html HTTP/1.1\r\n"
651 "Transfer-Encoding: chunked\r\n\r\n"),
652 MockWrite(ASYNC, 1, "0\r\n\r\n"),
653 };
654
655 // The size of the response body, as reflected in the Content-Length of the
656 // MockRead below.
657 const int kBodySize = 8;
658
659 MockRead reads[] = {
660 MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"),
661 MockRead(ASYNC, 3, "Content-Length: 8\r\n\r\n"),
662 MockRead(ASYNC, 4, "one.html"),
663 MockRead(SYNCHRONOUS, 0, 5), // EOF
664 };
665
666 ChunkedUploadDataStream upload_stream(0);
667 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
668
mmenkef22be0ee2015-06-05 15:39:02669 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
mmenke3dc8c88b2015-02-19 15:11:27670 scoped_ptr<ClientSocketHandle> socket_handle =
671 CreateConnectedSocketHandle(&data);
672
673 HttpRequestInfo request_info;
674 request_info.method = "GET";
675 request_info.url = GURL("http://localhost");
676 request_info.upload_data_stream = &upload_stream;
677
678 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
679 HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
680 BoundNetLog());
681
682 HttpRequestHeaders request_headers;
683 request_headers.SetHeader("Transfer-Encoding", "chunked");
684
685 HttpResponseInfo response_info;
686 TestCompletionCallback callback;
687 // This will attempt to Write() the initial request and headers, which will
688 // complete asynchronously.
689 ASSERT_EQ(ERR_IO_PENDING,
690 parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
691 &response_info, callback.callback()));
692
mmenke3dc8c88b2015-02-19 15:11:27693 // Now append the terminal 0-byte "chunk".
694 upload_stream.AppendData(nullptr, 0, true);
695 ASSERT_FALSE(callback.have_result());
696
mmenke3dc8c88b2015-02-19 15:11:27697 ASSERT_EQ(OK, callback.WaitForResult());
698
699 // Attempt to read the response status and the response headers.
700 ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:27701 ASSERT_GT(callback.WaitForResult(), 0);
702
703 // Finally, attempt to read the response body.
704 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
705 ASSERT_EQ(ERR_IO_PENDING,
706 parser.ReadResponseBody(body_buffer.get(), kBodySize,
707 callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:27708 ASSERT_EQ(kBodySize, callback.WaitForResult());
sclittlebe1ccf62015-09-02 19:40:36709
710 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
711 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), parser.received_bytes());
mmenke3dc8c88b2015-02-19 15:11:27712}
713
714// Test to ensure the HttpStreamParser state machine does not get confused
715// when there's only one "chunk" with 0 bytes, which was already appended before
716// the request was started.
717TEST(HttpStreamParser, SyncEmptyChunkedUpload) {
718 MockWrite writes[] = {
719 MockWrite(ASYNC, 0,
720 "GET /one.html HTTP/1.1\r\n"
721 "Transfer-Encoding: chunked\r\n\r\n"),
722 MockWrite(ASYNC, 1, "0\r\n\r\n"),
723 };
724
725 // The size of the response body, as reflected in the Content-Length of the
726 // MockRead below.
727 const int kBodySize = 8;
728
729 MockRead reads[] = {
730 MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"),
731 MockRead(ASYNC, 3, "Content-Length: 8\r\n\r\n"),
732 MockRead(ASYNC, 4, "one.html"),
733 MockRead(SYNCHRONOUS, 0, 5), // EOF
734 };
735
736 ChunkedUploadDataStream upload_stream(0);
737 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
738 // Append final empty chunk.
739 upload_stream.AppendData(nullptr, 0, true);
740
mmenkef22be0ee2015-06-05 15:39:02741 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
mmenke3dc8c88b2015-02-19 15:11:27742 scoped_ptr<ClientSocketHandle> socket_handle =
743 CreateConnectedSocketHandle(&data);
744
745 HttpRequestInfo request_info;
746 request_info.method = "GET";
747 request_info.url = GURL("http://localhost");
748 request_info.upload_data_stream = &upload_stream;
749
750 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
751 HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
752 BoundNetLog());
753
754 HttpRequestHeaders request_headers;
755 request_headers.SetHeader("Transfer-Encoding", "chunked");
756
757 HttpResponseInfo response_info;
758 TestCompletionCallback callback;
759 // This will attempt to Write() the initial request and headers, which will
760 // complete asynchronously.
761 ASSERT_EQ(ERR_IO_PENDING,
762 parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
763 &response_info, callback.callback()));
764
765 // Complete writing the request headers and body.
mmenke3dc8c88b2015-02-19 15:11:27766 ASSERT_EQ(OK, callback.WaitForResult());
767
768 // Attempt to read the response status and the response headers.
769 ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:27770 ASSERT_GT(callback.WaitForResult(), 0);
771
772 // Finally, attempt to read the response body.
773 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
774 ASSERT_EQ(ERR_IO_PENDING,
775 parser.ReadResponseBody(body_buffer.get(), kBodySize,
776 callback.callback()));
mmenke3dc8c88b2015-02-19 15:11:27777 ASSERT_EQ(kBodySize, callback.WaitForResult());
sclittlebe1ccf62015-09-02 19:40:36778
779 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
780 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), parser.received_bytes());
[email protected]4750937f2012-06-15 20:44:21781}
782
[email protected]9c18dbc2013-05-29 19:06:53783TEST(HttpStreamParser, TruncatedHeaders) {
784 MockRead truncated_status_reads[] = {
785 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 20"),
786 MockRead(SYNCHRONOUS, 0, 2), // EOF
787 };
788
789 MockRead truncated_after_status_reads[] = {
790 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\n"),
791 MockRead(SYNCHRONOUS, 0, 2), // EOF
792 };
793
794 MockRead truncated_in_header_reads[] = {
795 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHead"),
796 MockRead(SYNCHRONOUS, 0, 2), // EOF
797 };
798
799 MockRead truncated_after_header_reads[] = {
800 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n"),
801 MockRead(SYNCHRONOUS, 0, 2), // EOF
802 };
803
804 MockRead truncated_after_final_newline_reads[] = {
805 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r"),
806 MockRead(SYNCHRONOUS, 0, 2), // EOF
807 };
808
809 MockRead not_truncated_reads[] = {
810 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r\n"),
811 MockRead(SYNCHRONOUS, 0, 2), // EOF
812 };
813
814 MockRead* reads[] = {
815 truncated_status_reads,
816 truncated_after_status_reads,
817 truncated_in_header_reads,
818 truncated_after_header_reads,
819 truncated_after_final_newline_reads,
820 not_truncated_reads,
821 };
822
823 MockWrite writes[] = {
824 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n\r\n"),
825 };
826
zmo9528c9f42015-08-04 22:12:08827 enum {
828 HTTP = 0,
829 HTTPS,
830 NUM_PROTOCOLS,
831 };
[email protected]9c18dbc2013-05-29 19:06:53832
zmo9528c9f42015-08-04 22:12:08833 for (size_t protocol = 0; protocol < NUM_PROTOCOLS; protocol++) {
834 SCOPED_TRACE(protocol);
[email protected]9c18dbc2013-05-29 19:06:53835
zmo9528c9f42015-08-04 22:12:08836 for (size_t i = 0; i < arraysize(reads); i++) {
837 SCOPED_TRACE(i);
838 SequencedSocketData data(reads[i], 2, writes, arraysize(writes));
839 scoped_ptr<ClientSocketHandle> socket_handle(
840 CreateConnectedSocketHandle(&data));
[email protected]9c18dbc2013-05-29 19:06:53841
zmo9528c9f42015-08-04 22:12:08842 HttpRequestInfo request_info;
843 request_info.method = "GET";
844 if (protocol == HTTP) {
845 request_info.url = GURL("http://localhost");
846 } else {
847 request_info.url = GURL("https://localhost");
848 }
849 request_info.load_flags = LOAD_NORMAL;
[email protected]9c18dbc2013-05-29 19:06:53850
zmo9528c9f42015-08-04 22:12:08851 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
852 HttpStreamParser parser(
853 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog());
854
855 HttpRequestHeaders request_headers;
856 HttpResponseInfo response_info;
857 TestCompletionCallback callback;
858 ASSERT_EQ(OK, parser.SendRequest("GET / HTTP/1.1\r\n", request_headers,
859 &response_info, callback.callback()));
860
861 int rv = parser.ReadResponseHeaders(callback.callback());
sclittlebe1ccf62015-09-02 19:40:36862 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)),
863 parser.sent_bytes());
zmo9528c9f42015-08-04 22:12:08864 if (i == arraysize(reads) - 1) {
865 EXPECT_EQ(OK, rv);
866 EXPECT_TRUE(response_info.headers.get());
sclittlebe1ccf62015-09-02 19:40:36867 EXPECT_EQ(CountReadBytes(reads[i], 2), parser.received_bytes());
zmo9528c9f42015-08-04 22:12:08868 } else {
869 if (protocol == HTTP) {
870 EXPECT_EQ(ERR_CONNECTION_CLOSED, rv);
871 EXPECT_TRUE(response_info.headers.get());
sclittlebe1ccf62015-09-02 19:40:36872 EXPECT_EQ(CountReadBytes(reads[i], 2), parser.received_bytes());
zmo9528c9f42015-08-04 22:12:08873 } else {
874 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED, rv);
875 EXPECT_FALSE(response_info.headers.get());
sclittlebe1ccf62015-09-02 19:40:36876 EXPECT_EQ(0, parser.received_bytes());
zmo9528c9f42015-08-04 22:12:08877 }
878 }
[email protected]9c18dbc2013-05-29 19:06:53879 }
880 }
881}
882
[email protected]df8e6382013-11-07 00:19:06883// Confirm that on 101 response, the headers are parsed but the data that
884// follows remains in the buffer.
885TEST(HttpStreamParser, Websocket101Response) {
886 MockRead reads[] = {
887 MockRead(SYNCHRONOUS, 1,
888 "HTTP/1.1 101 Switching Protocols\r\n"
889 "Upgrade: websocket\r\n"
890 "Connection: Upgrade\r\n"
891 "\r\n"
892 "a fake websocket frame"),
893 };
894
895 MockWrite writes[] = {
896 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n\r\n"),
897 };
898
mmenkef22be0ee2015-06-05 15:39:02899 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
900 scoped_ptr<ClientSocketHandle> socket_handle =
901 CreateConnectedSocketHandle(&data);
[email protected]df8e6382013-11-07 00:19:06902
903 HttpRequestInfo request_info;
904 request_info.method = "GET";
905 request_info.url = GURL("http://localhost");
906 request_info.load_flags = LOAD_NORMAL;
907
908 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
909 HttpStreamParser parser(
910 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog());
911
912 HttpRequestHeaders request_headers;
913 HttpResponseInfo response_info;
mmenkef22be0ee2015-06-05 15:39:02914 TestCompletionCallback callback;
915 ASSERT_EQ(OK, parser.SendRequest("GET / HTTP/1.1\r\n", request_headers,
916 &response_info, callback.callback()));
[email protected]df8e6382013-11-07 00:19:06917
mmenkef22be0ee2015-06-05 15:39:02918 EXPECT_EQ(OK, parser.ReadResponseHeaders(callback.callback()));
[email protected]df8e6382013-11-07 00:19:06919 ASSERT_TRUE(response_info.headers.get());
920 EXPECT_EQ(101, response_info.headers->response_code());
921 EXPECT_TRUE(response_info.headers->HasHeaderValue("Connection", "Upgrade"));
922 EXPECT_TRUE(response_info.headers->HasHeaderValue("Upgrade", "websocket"));
923 EXPECT_EQ(read_buffer->capacity(), read_buffer->offset());
924 EXPECT_EQ("a fake websocket frame",
925 base::StringPiece(read_buffer->StartOfBuffer(),
926 read_buffer->capacity()));
sclittlebe1ccf62015-09-02 19:40:36927
928 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
929 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)) -
930 static_cast<int64_t>(strlen("a fake websocket frame")),
931 parser.received_bytes());
[email protected]df8e6382013-11-07 00:19:06932}
933
[email protected]390489b2013-12-09 10:49:01934// Helper class for constructing HttpStreamParser and running GET requests.
935class SimpleGetRunner {
936 public:
937 SimpleGetRunner() : read_buffer_(new GrowableIOBuffer), sequence_number_(0) {
938 writes_.push_back(MockWrite(
939 SYNCHRONOUS, sequence_number_++, "GET / HTTP/1.1\r\n\r\n"));
940 }
941
942 HttpStreamParser* parser() { return parser_.get(); }
943 GrowableIOBuffer* read_buffer() { return read_buffer_.get(); }
944 HttpResponseInfo* response_info() { return &response_info_; }
945
946 void AddInitialData(const std::string& data) {
947 int offset = read_buffer_->offset();
948 int size = data.size();
949 read_buffer_->SetCapacity(offset + size);
950 memcpy(read_buffer_->StartOfBuffer() + offset, data.data(), size);
951 read_buffer_->set_offset(offset + size);
952 }
953
954 void AddRead(const std::string& data) {
955 reads_.push_back(MockRead(SYNCHRONOUS, sequence_number_++, data.data()));
956 }
957
958 void SetupParserAndSendRequest() {
959 reads_.push_back(MockRead(SYNCHRONOUS, 0, sequence_number_++)); // EOF
960
mmenkef22be0ee2015-06-05 15:39:02961 data_.reset(new SequencedSocketData(&reads_.front(), reads_.size(),
962 &writes_.front(), writes_.size()));
963 socket_handle_ = CreateConnectedSocketHandle(data_.get());
[email protected]390489b2013-12-09 10:49:01964
965 request_info_.method = "GET";
966 request_info_.url = GURL("http://localhost");
967 request_info_.load_flags = LOAD_NORMAL;
968
969 parser_.reset(new HttpStreamParser(
970 socket_handle_.get(), &request_info_, read_buffer(), BoundNetLog()));
971
mmenkef22be0ee2015-06-05 15:39:02972 TestCompletionCallback callback;
973 ASSERT_EQ(OK, parser_->SendRequest("GET / HTTP/1.1\r\n", request_headers_,
974 &response_info_, callback.callback()));
[email protected]390489b2013-12-09 10:49:01975 }
976
977 void ReadHeaders() {
978 TestCompletionCallback callback;
979 EXPECT_EQ(OK, parser_->ReadResponseHeaders(callback.callback()));
980 }
981
982 void ReadBody(int user_buf_len, int* read_lengths) {
983 TestCompletionCallback callback;
984 scoped_refptr<IOBuffer> buffer = new IOBuffer(user_buf_len);
985 int rv;
986 int i = 0;
987 while (true) {
dcheng48459ac22014-08-26 00:46:41988 rv = parser_->ReadResponseBody(
989 buffer.get(), user_buf_len, callback.callback());
[email protected]390489b2013-12-09 10:49:01990 EXPECT_EQ(read_lengths[i], rv);
991 i++;
992 if (rv <= 0)
993 return;
994 }
995 }
996
997 private:
998 HttpRequestHeaders request_headers_;
999 HttpResponseInfo response_info_;
1000 HttpRequestInfo request_info_;
1001 scoped_refptr<GrowableIOBuffer> read_buffer_;
1002 std::vector<MockRead> reads_;
1003 std::vector<MockWrite> writes_;
1004 scoped_ptr<ClientSocketHandle> socket_handle_;
mmenkef22be0ee2015-06-05 15:39:021005 scoped_ptr<SequencedSocketData> data_;
[email protected]390489b2013-12-09 10:49:011006 scoped_ptr<HttpStreamParser> parser_;
1007 int sequence_number_;
1008};
1009
1010// Test that HTTP/0.9 response size is correctly calculated.
1011TEST(HttpStreamParser, ReceivedBytesNoHeaders) {
1012 std::string response = "hello\r\nworld\r\n";
1013
1014 SimpleGetRunner get_runner;
1015 get_runner.AddRead(response);
1016 get_runner.SetupParserAndSendRequest();
1017 get_runner.ReadHeaders();
1018 EXPECT_EQ(0, get_runner.parser()->received_bytes());
[email protected]3cb5fc22013-12-12 19:40:561019 int response_size = response.size();
[email protected]390489b2013-12-09 10:49:011020 int read_lengths[] = {response_size, 0};
1021 get_runner.ReadBody(response_size, read_lengths);
1022 EXPECT_EQ(response_size, get_runner.parser()->received_bytes());
1023}
1024
1025// Test basic case where there is no keep-alive or extra data from the socket,
1026// and the entire response is received in a single read.
1027TEST(HttpStreamParser, ReceivedBytesNormal) {
1028 std::string headers = "HTTP/1.1 200 OK\r\n"
1029 "Content-Length: 7\r\n\r\n";
1030 std::string body = "content";
1031 std::string response = headers + body;
1032
1033 SimpleGetRunner get_runner;
1034 get_runner.AddRead(response);
1035 get_runner.SetupParserAndSendRequest();
1036 get_runner.ReadHeaders();
1037 int64 headers_size = headers.size();
1038 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes());
[email protected]3cb5fc22013-12-12 19:40:561039 int body_size = body.size();
[email protected]390489b2013-12-09 10:49:011040 int read_lengths[] = {body_size, 0};
1041 get_runner.ReadBody(body_size, read_lengths);
1042 int64 response_size = response.size();
1043 EXPECT_EQ(response_size, get_runner.parser()->received_bytes());
1044}
1045
1046// Test that bytes that represent "next" response are not counted
1047// as current response "received_bytes".
1048TEST(HttpStreamParser, ReceivedBytesExcludesNextResponse) {
1049 std::string headers = "HTTP/1.1 200 OK\r\n"
1050 "Content-Length: 8\r\n\r\n";
1051 std::string body = "content8";
1052 std::string response = headers + body;
1053 std::string next_response = "HTTP/1.1 200 OK\r\n\r\nFOO";
1054 std::string data = response + next_response;
1055
1056 SimpleGetRunner get_runner;
1057 get_runner.AddRead(data);
1058 get_runner.SetupParserAndSendRequest();
1059 get_runner.ReadHeaders();
1060 EXPECT_EQ(39, get_runner.parser()->received_bytes());
1061 int64 headers_size = headers.size();
1062 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes());
[email protected]3cb5fc22013-12-12 19:40:561063 int body_size = body.size();
[email protected]390489b2013-12-09 10:49:011064 int read_lengths[] = {body_size, 0};
1065 get_runner.ReadBody(body_size, read_lengths);
1066 int64 response_size = response.size();
1067 EXPECT_EQ(response_size, get_runner.parser()->received_bytes());
1068 int64 next_response_size = next_response.size();
1069 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset());
1070}
1071
1072// Test that "received_bytes" calculation works fine when last read
1073// contains more data than requested by user.
1074// We send data in two reads:
1075// 1) Headers + beginning of response
1076// 2) remaining part of response + next response start
1077// We setup user read buffer so it fully accepts the beginnig of response
1078// body, but it is larger that remaining part of body.
1079TEST(HttpStreamParser, ReceivedBytesMultiReadExcludesNextResponse) {
1080 std::string headers = "HTTP/1.1 200 OK\r\n"
1081 "Content-Length: 36\r\n\r\n";
1082 int64 user_buf_len = 32;
1083 std::string body_start = std::string(user_buf_len, '#');
[email protected]3cb5fc22013-12-12 19:40:561084 int body_start_size = body_start.size();
[email protected]390489b2013-12-09 10:49:011085 EXPECT_EQ(user_buf_len, body_start_size);
1086 std::string response_start = headers + body_start;
1087 std::string body_end = "abcd";
1088 std::string next_response = "HTTP/1.1 200 OK\r\n\r\nFOO";
1089 std::string response_end = body_end + next_response;
1090
1091 SimpleGetRunner get_runner;
1092 get_runner.AddRead(response_start);
1093 get_runner.AddRead(response_end);
1094 get_runner.SetupParserAndSendRequest();
1095 get_runner.ReadHeaders();
1096 int64 headers_size = headers.size();
1097 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes());
[email protected]3cb5fc22013-12-12 19:40:561098 int body_end_size = body_end.size();
[email protected]390489b2013-12-09 10:49:011099 int read_lengths[] = {body_start_size, body_end_size, 0};
1100 get_runner.ReadBody(body_start_size, read_lengths);
1101 int64 response_size = response_start.size() + body_end_size;
1102 EXPECT_EQ(response_size, get_runner.parser()->received_bytes());
1103 int64 next_response_size = next_response.size();
1104 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset());
1105}
1106
1107// Test that "received_bytes" calculation works fine when there is no
1108// network activity at all; that is when all data is read from read buffer.
1109// In this case read buffer contains two responses. We expect that only
1110// bytes that correspond to the first one are taken into account.
1111TEST(HttpStreamParser, ReceivedBytesFromReadBufExcludesNextResponse) {
1112 std::string headers = "HTTP/1.1 200 OK\r\n"
1113 "Content-Length: 7\r\n\r\n";
1114 std::string body = "content";
1115 std::string response = headers + body;
1116 std::string next_response = "HTTP/1.1 200 OK\r\n\r\nFOO";
1117 std::string data = response + next_response;
1118
1119 SimpleGetRunner get_runner;
1120 get_runner.AddInitialData(data);
1121 get_runner.SetupParserAndSendRequest();
1122 get_runner.ReadHeaders();
1123 int64 headers_size = headers.size();
1124 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes());
[email protected]3cb5fc22013-12-12 19:40:561125 int body_size = body.size();
[email protected]390489b2013-12-09 10:49:011126 int read_lengths[] = {body_size, 0};
1127 get_runner.ReadBody(body_size, read_lengths);
1128 int64 response_size = response.size();
1129 EXPECT_EQ(response_size, get_runner.parser()->received_bytes());
1130 int64 next_response_size = next_response.size();
1131 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset());
1132}
1133
1134// Test calculating "received_bytes" when part of request has been already
1135// loaded and placed to read buffer by previous stream parser.
1136TEST(HttpStreamParser, ReceivedBytesUseReadBuf) {
1137 std::string buffer = "HTTP/1.1 200 OK\r\n";
1138 std::string remaining_headers = "Content-Length: 7\r\n\r\n";
1139 int64 headers_size = buffer.size() + remaining_headers.size();
1140 std::string body = "content";
1141 std::string response = remaining_headers + body;
1142
1143 SimpleGetRunner get_runner;
1144 get_runner.AddInitialData(buffer);
1145 get_runner.AddRead(response);
1146 get_runner.SetupParserAndSendRequest();
1147 get_runner.ReadHeaders();
1148 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes());
[email protected]3cb5fc22013-12-12 19:40:561149 int body_size = body.size();
[email protected]390489b2013-12-09 10:49:011150 int read_lengths[] = {body_size, 0};
1151 get_runner.ReadBody(body_size, read_lengths);
1152 EXPECT_EQ(headers_size + body_size, get_runner.parser()->received_bytes());
1153 EXPECT_EQ(0, get_runner.read_buffer()->offset());
1154}
1155
1156// Test the case when the resulting read_buf contains both unused bytes and
1157// bytes ejected by chunked-encoding filter.
1158TEST(HttpStreamParser, ReceivedBytesChunkedTransferExcludesNextResponse) {
1159 std::string response = "HTTP/1.1 200 OK\r\n"
1160 "Transfer-Encoding: chunked\r\n\r\n"
1161 "7\r\nChunk 1\r\n"
1162 "8\r\nChunky 2\r\n"
1163 "6\r\nTest 3\r\n"
1164 "0\r\n\r\n";
1165 std::string next_response = "foo bar\r\n";
1166 std::string data = response + next_response;
1167
1168 SimpleGetRunner get_runner;
1169 get_runner.AddInitialData(data);
1170 get_runner.SetupParserAndSendRequest();
1171 get_runner.ReadHeaders();
1172 int read_lengths[] = {4, 3, 6, 2, 6, 0};
1173 get_runner.ReadBody(7, read_lengths);
1174 int64 response_size = response.size();
1175 EXPECT_EQ(response_size, get_runner.parser()->received_bytes());
1176 int64 next_response_size = next_response.size();
1177 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset());
1178}
1179
1180// Test that data transfered in multiple reads is correctly processed.
1181// We feed data into 4-bytes reads. Also we set length of read
1182// buffer to 5-bytes to test all possible buffer misaligments.
1183TEST(HttpStreamParser, ReceivedBytesMultipleReads) {
1184 std::string headers = "HTTP/1.1 200 OK\r\n"
1185 "Content-Length: 33\r\n\r\n";
1186 std::string body = "foo bar baz\r\n"
1187 "sputnik mir babushka";
1188 std::string response = headers + body;
1189
1190 size_t receive_length = 4;
1191 std::vector<std::string> blocks;
1192 for (size_t i = 0; i < response.size(); i += receive_length) {
1193 size_t length = std::min(receive_length, response.size() - i);
1194 blocks.push_back(response.substr(i, length));
1195 }
1196
1197 SimpleGetRunner get_runner;
[email protected]3cb5fc22013-12-12 19:40:561198 for (std::vector<std::string>::size_type i = 0; i < blocks.size(); ++i)
[email protected]390489b2013-12-09 10:49:011199 get_runner.AddRead(blocks[i]);
1200 get_runner.SetupParserAndSendRequest();
1201 get_runner.ReadHeaders();
1202 int64 headers_size = headers.size();
1203 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes());
1204 int read_lengths[] = {1, 4, 4, 4, 4, 4, 4, 4, 4, 0};
1205 get_runner.ReadBody(receive_length + 1, read_lengths);
1206 int64 response_size = response.size();
1207 EXPECT_EQ(response_size, get_runner.parser()->received_bytes());
1208}
1209
1210// Test that "continue" HTTP header is counted as "received_bytes".
1211TEST(HttpStreamParser, ReceivedBytesIncludesContinueHeader) {
1212 std::string status100 = "HTTP/1.1 100 OK\r\n\r\n";
1213 std::string headers = "HTTP/1.1 200 OK\r\n"
1214 "Content-Length: 7\r\n\r\n";
1215 int64 headers_size = status100.size() + headers.size();
1216 std::string body = "content";
1217 std::string response = headers + body;
1218
1219 SimpleGetRunner get_runner;
1220 get_runner.AddRead(status100);
1221 get_runner.AddRead(response);
1222 get_runner.SetupParserAndSendRequest();
1223 get_runner.ReadHeaders();
1224 EXPECT_EQ(100, get_runner.response_info()->headers->response_code());
1225 int64 status100_size = status100.size();
1226 EXPECT_EQ(status100_size, get_runner.parser()->received_bytes());
1227 get_runner.ReadHeaders();
1228 EXPECT_EQ(200, get_runner.response_info()->headers->response_code());
1229 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes());
1230 int64 response_size = headers_size + body.size();
[email protected]3cb5fc22013-12-12 19:40:561231 int body_size = body.size();
[email protected]390489b2013-12-09 10:49:011232 int read_lengths[] = {body_size, 0};
1233 get_runner.ReadBody(body_size, read_lengths);
1234 EXPECT_EQ(response_size, get_runner.parser()->received_bytes());
1235}
1236
[email protected]ecab6e052014-05-16 14:58:121237// Test that an HttpStreamParser can be read from after it's received headers
1238// and data structures owned by its owner have been deleted. This happens
1239// when a ResponseBodyDrainer is used.
1240TEST(HttpStreamParser, ReadAfterUnownedObjectsDestroyed) {
1241 MockWrite writes[] = {
1242 MockWrite(SYNCHRONOUS, 0,
1243 "GET /foo.html HTTP/1.1\r\n\r\n"),
[email protected]ecab6e052014-05-16 14:58:121244 };
1245
1246 const int kBodySize = 1;
1247 MockRead reads[] = {
mmenkef22be0ee2015-06-05 15:39:021248 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
sclittlebe1ccf62015-09-02 19:40:361249 MockRead(SYNCHRONOUS, 2, "Content-Length: 1\r\n"),
mmenkef22be0ee2015-06-05 15:39:021250 MockRead(SYNCHRONOUS, 3, "Connection: Keep-Alive\r\n\r\n"),
1251 MockRead(SYNCHRONOUS, 4, "1"),
1252 MockRead(SYNCHRONOUS, 0, 5), // EOF
[email protected]ecab6e052014-05-16 14:58:121253 };
1254
mmenkef22be0ee2015-06-05 15:39:021255 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
1256 scoped_ptr<ClientSocketHandle> socket_handle =
1257 CreateConnectedSocketHandle(&data);
[email protected]ecab6e052014-05-16 14:58:121258
1259 scoped_ptr<HttpRequestInfo> request_info(new HttpRequestInfo());
1260 request_info->method = "GET";
1261 request_info->url = GURL("http://somewhere/foo.html");
1262
1263 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
1264 HttpStreamParser parser(socket_handle.get(), request_info.get(),
1265 read_buffer.get(), BoundNetLog());
1266
1267 scoped_ptr<HttpRequestHeaders> request_headers(new HttpRequestHeaders());
1268 scoped_ptr<HttpResponseInfo> response_info(new HttpResponseInfo());
mmenkef22be0ee2015-06-05 15:39:021269 TestCompletionCallback callback;
[email protected]ecab6e052014-05-16 14:58:121270 ASSERT_EQ(OK, parser.SendRequest("GET /foo.html HTTP/1.1\r\n",
1271 *request_headers, response_info.get(), callback.callback()));
1272 ASSERT_EQ(OK, parser.ReadResponseHeaders(callback.callback()));
1273
1274 // If the object that owns the HttpStreamParser is deleted, it takes the
1275 // objects passed to the HttpStreamParser with it.
1276 request_info.reset();
1277 request_headers.reset();
1278 response_info.reset();
1279
1280 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
1281 ASSERT_EQ(kBodySize, parser.ReadResponseBody(
1282 body_buffer.get(), kBodySize, callback.callback()));
sclittlebe1ccf62015-09-02 19:40:361283
1284 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), parser.sent_bytes());
1285 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), parser.received_bytes());
[email protected]ecab6e052014-05-16 14:58:121286}
1287
[email protected]390489b2013-12-09 10:49:011288} // namespace
[email protected]df8e6382013-11-07 00:19:061289
[email protected]6db833d12012-01-21 00:45:191290} // namespace net