blob: 30384841bbfdc76f1666af05dfcdc456be27a0d9 [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
[email protected]7a1fcff2012-01-24 01:07:497#include "base/file_util.h"
[email protected]57999812013-02-24 05:40:528#include "base/files/file_path.h"
[email protected]ea1a3f62012-11-16 20:34:239#include "base/files/scoped_temp_dir.h"
[email protected]4750937f2012-06-15 20:44:2110#include "base/memory/ref_counted.h"
[email protected]0f972882013-09-20 04:34:2011#include "base/run_loop.h"
[email protected]d069c11a2013-04-13 00:01:5512#include "base/strings/string_piece.h"
[email protected]125ef482013-06-11 18:32:4713#include "base/strings/stringprintf.h"
[email protected]4750937f2012-06-15 20:44:2114#include "net/base/io_buffer.h"
[email protected]6db833d12012-01-21 00:45:1915#include "net/base/net_errors.h"
[email protected]4750937f2012-06-15 20:44:2116#include "net/base/test_completion_callback.h"
[email protected]b2d26cfd2012-12-11 10:36:0617#include "net/base/upload_bytes_element_reader.h"
[email protected]7a1fcff2012-01-24 01:07:4918#include "net/base/upload_data_stream.h"
[email protected]b2d26cfd2012-12-11 10:36:0619#include "net/base/upload_file_element_reader.h"
[email protected]4750937f2012-06-15 20:44:2120#include "net/http/http_request_headers.h"
21#include "net/http/http_request_info.h"
[email protected]df8e6382013-11-07 00:19:0622#include "net/http/http_response_headers.h"
[email protected]4750937f2012-06-15 20:44:2123#include "net/http/http_response_info.h"
24#include "net/socket/client_socket_handle.h"
25#include "net/socket/socket_test_util.h"
[email protected]6db833d12012-01-21 00:45:1926#include "testing/gtest/include/gtest/gtest.h"
[email protected]f89276a72013-07-12 06:41:5427#include "url/gurl.h"
[email protected]6db833d12012-01-21 00:45:1928
29namespace net {
30
31const size_t kOutputSize = 1024; // Just large enough for this test.
32// The number of bytes that can fit in a buffer of kOutputSize.
33const size_t kMaxPayloadSize =
34 kOutputSize - HttpStreamParser::kChunkHeaderFooterSize;
35
36// The empty payload is how the last chunk is encoded.
37TEST(HttpStreamParser, EncodeChunk_EmptyPayload) {
38 char output[kOutputSize];
39
40 const base::StringPiece kPayload = "";
41 const base::StringPiece kExpected = "0\r\n\r\n";
42 const int num_bytes_written =
43 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
44 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
45 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
46}
47
48TEST(HttpStreamParser, EncodeChunk_ShortPayload) {
49 char output[kOutputSize];
50
51 const std::string kPayload("foo\x00\x11\x22", 6);
52 // 11 = payload size + sizeof("6") + CRLF x 2.
53 const std::string kExpected("6\r\nfoo\x00\x11\x22\r\n", 11);
54 const int num_bytes_written =
55 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
56 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
57 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
58}
59
60TEST(HttpStreamParser, EncodeChunk_LargePayload) {
61 char output[kOutputSize];
62
63 const std::string kPayload(1000, '\xff'); // '\xff' x 1000.
64 // 3E8 = 1000 in hex.
65 const std::string kExpected = "3E8\r\n" + kPayload + "\r\n";
66 const int num_bytes_written =
67 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
68 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
69 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
70}
71
72TEST(HttpStreamParser, EncodeChunk_FullPayload) {
73 char output[kOutputSize];
74
75 const std::string kPayload(kMaxPayloadSize, '\xff');
76 // 3F4 = 1012 in hex.
77 const std::string kExpected = "3F4\r\n" + kPayload + "\r\n";
78 const int num_bytes_written =
79 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
80 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
81 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
82}
83
84TEST(HttpStreamParser, EncodeChunk_TooLargePayload) {
85 char output[kOutputSize];
86
87 // The payload is one byte larger the output buffer size.
88 const std::string kPayload(kMaxPayloadSize + 1, '\xff');
89 const int num_bytes_written =
90 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
91 ASSERT_EQ(ERR_INVALID_ARGUMENT, num_bytes_written);
92}
93
[email protected]75577ec2012-01-24 23:41:5094TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_NoBody) {
[email protected]7a1fcff2012-01-24 01:07:4995 // Shouldn't be merged if upload data is non-existent.
[email protected]75577ec2012-01-24 23:41:5096 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
97 "some header", NULL));
[email protected]7a1fcff2012-01-24 01:07:4998}
99
[email protected]75577ec2012-01-24 23:41:50100TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_EmptyBody) {
[email protected]b2d26cfd2012-12-11 10:36:06101 ScopedVector<UploadElementReader> element_readers;
[email protected]96c77a72013-09-24 09:49:20102 scoped_ptr<UploadDataStream> body(
103 new UploadDataStream(element_readers.Pass(), 0));
[email protected]4db27d82012-12-20 11:50:24104 ASSERT_EQ(OK, body->Init(CompletionCallback()));
[email protected]7a1fcff2012-01-24 01:07:49105 // Shouldn't be merged if upload data is empty.
[email protected]75577ec2012-01-24 23:41:50106 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
107 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49108}
109
[email protected]75577ec2012-01-24 23:41:50110TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_ChunkedBody) {
[email protected]7a1fcff2012-01-24 01:07:49111 const std::string payload = "123";
[email protected]b2d26cfd2012-12-11 10:36:06112 scoped_ptr<UploadDataStream> body(
113 new UploadDataStream(UploadDataStream::CHUNKED, 0));
114 body->AppendChunk(payload.data(), payload.size(), true);
[email protected]4db27d82012-12-20 11:50:24115 ASSERT_EQ(OK, body->Init(CompletionCallback()));
[email protected]7a1fcff2012-01-24 01:07:49116 // Shouldn't be merged if upload data carries chunked data.
[email protected]75577ec2012-01-24 23:41:50117 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
118 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49119}
120
[email protected]75577ec2012-01-24 23:41:50121TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_FileBody) {
[email protected]0f972882013-09-20 04:34:20122 {
123 ScopedVector<UploadElementReader> element_readers;
[email protected]7a1fcff2012-01-24 01:07:49124
[email protected]0f972882013-09-20 04:34:20125 // Create an empty temporary file.
126 base::ScopedTempDir temp_dir;
127 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
128 base::FilePath temp_file_path;
129 ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir.path(),
130 &temp_file_path));
[email protected]7a1fcff2012-01-24 01:07:49131
[email protected]0f972882013-09-20 04:34:20132 element_readers.push_back(
133 new UploadFileElementReader(base::MessageLoopProxy::current().get(),
134 temp_file_path,
135 0,
136 0,
137 base::Time()));
[email protected]7a1fcff2012-01-24 01:07:49138
[email protected]0f972882013-09-20 04:34:20139 scoped_ptr<UploadDataStream> body(
[email protected]96c77a72013-09-24 09:49:20140 new UploadDataStream(element_readers.Pass(), 0));
[email protected]0f972882013-09-20 04:34:20141 TestCompletionCallback callback;
142 ASSERT_EQ(ERR_IO_PENDING, body->Init(callback.callback()));
143 ASSERT_EQ(OK, callback.WaitForResult());
144 // Shouldn't be merged if upload data carries a file, as it's not in-memory.
145 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
146 "some header", body.get()));
147 }
148 // UploadFileElementReaders may post clean-up tasks on destruction.
149 base::RunLoop().RunUntilIdle();
[email protected]7a1fcff2012-01-24 01:07:49150}
151
[email protected]75577ec2012-01-24 23:41:50152TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_SmallBodyInMemory) {
[email protected]b2d26cfd2012-12-11 10:36:06153 ScopedVector<UploadElementReader> element_readers;
[email protected]7a1fcff2012-01-24 01:07:49154 const std::string payload = "123";
[email protected]b2d26cfd2012-12-11 10:36:06155 element_readers.push_back(new UploadBytesElementReader(
156 payload.data(), payload.size()));
[email protected]7a1fcff2012-01-24 01:07:49157
[email protected]96c77a72013-09-24 09:49:20158 scoped_ptr<UploadDataStream> body(
159 new UploadDataStream(element_readers.Pass(), 0));
[email protected]4db27d82012-12-20 11:50:24160 ASSERT_EQ(OK, body->Init(CompletionCallback()));
[email protected]7a1fcff2012-01-24 01:07:49161 // Yes, should be merged if the in-memory body is small here.
[email protected]75577ec2012-01-24 23:41:50162 ASSERT_TRUE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
163 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49164}
165
[email protected]75577ec2012-01-24 23:41:50166TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_LargeBodyInMemory) {
[email protected]b2d26cfd2012-12-11 10:36:06167 ScopedVector<UploadElementReader> element_readers;
[email protected]7a1fcff2012-01-24 01:07:49168 const std::string payload(10000, 'a'); // 'a' x 10000.
[email protected]b2d26cfd2012-12-11 10:36:06169 element_readers.push_back(new UploadBytesElementReader(
170 payload.data(), payload.size()));
[email protected]7a1fcff2012-01-24 01:07:49171
[email protected]96c77a72013-09-24 09:49:20172 scoped_ptr<UploadDataStream> body(
173 new UploadDataStream(element_readers.Pass(), 0));
[email protected]4db27d82012-12-20 11:50:24174 ASSERT_EQ(OK, body->Init(CompletionCallback()));
[email protected]7a1fcff2012-01-24 01:07:49175 // Shouldn't be merged if the in-memory body is large here.
[email protected]75577ec2012-01-24 23:41:50176 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
177 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49178}
179
[email protected]4750937f2012-06-15 20:44:21180// Test to ensure the HttpStreamParser state machine does not get confused
181// when sending a request with a chunked body, where chunks become available
182// asynchronously, over a socket where writes may also complete
183// asynchronously.
184// This is a regression test for http://crbug.com/132243
185TEST(HttpStreamParser, AsyncChunkAndAsyncSocket) {
186 // The chunks that will be written in the request, as reflected in the
187 // MockWrites below.
188 static const char kChunk1[] = "Chunk 1";
189 static const char kChunk2[] = "Chunky 2";
190 static const char kChunk3[] = "Test 3";
191
192 MockWrite writes[] = {
193 MockWrite(ASYNC, 0,
194 "GET /one.html HTTP/1.1\r\n"
195 "Host: localhost\r\n"
196 "Transfer-Encoding: chunked\r\n"
197 "Connection: keep-alive\r\n\r\n"),
198 MockWrite(ASYNC, 1, "7\r\nChunk 1\r\n"),
199 MockWrite(ASYNC, 2, "8\r\nChunky 2\r\n"),
200 MockWrite(ASYNC, 3, "6\r\nTest 3\r\n"),
201 MockWrite(ASYNC, 4, "0\r\n\r\n"),
202 };
203
204 // The size of the response body, as reflected in the Content-Length of the
205 // MockRead below.
206 static const int kBodySize = 8;
207
208 MockRead reads[] = {
209 MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"),
210 MockRead(ASYNC, 6, "Content-Length: 8\r\n\r\n"),
211 MockRead(ASYNC, 7, "one.html"),
[email protected]d55b30a2012-07-21 00:35:39212 MockRead(SYNCHRONOUS, 0, 8), // EOF
[email protected]4750937f2012-06-15 20:44:21213 };
214
[email protected]b2d26cfd2012-12-11 10:36:06215 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0);
216 upload_stream.AppendChunk(kChunk1, arraysize(kChunk1) - 1, false);
[email protected]4db27d82012-12-20 11:50:24217 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback()));
[email protected]9a963892012-11-01 11:48:13218
[email protected]dd54bd82012-07-19 23:44:57219 DeterministicSocketData data(reads, arraysize(reads),
220 writes, arraysize(writes));
221 data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
[email protected]4750937f2012-06-15 20:44:21222
223 scoped_ptr<DeterministicMockTCPClientSocket> transport(
[email protected]dd54bd82012-07-19 23:44:57224 new DeterministicMockTCPClientSocket(NULL, &data));
[email protected]0edce6a2013-05-08 18:02:40225 data.set_delegate(transport->AsWeakPtr());
[email protected]4750937f2012-06-15 20:44:21226
227 TestCompletionCallback callback;
228 int rv = transport->Connect(callback.callback());
229 rv = callback.GetResult(rv);
230 ASSERT_EQ(OK, rv);
231
232 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
[email protected]18ccfdb2013-08-15 00:13:44233 socket_handle->SetSocket(transport.PassAs<StreamSocket>());
[email protected]4750937f2012-06-15 20:44:21234
235 HttpRequestInfo request_info;
236 request_info.method = "GET";
237 request_info.url = GURL("http://localhost");
238 request_info.load_flags = LOAD_NORMAL;
[email protected]bf3eb002012-11-15 05:50:11239 request_info.upload_data_stream = &upload_stream;
[email protected]4750937f2012-06-15 20:44:21240
241 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
[email protected]90499482013-06-01 00:39:50242 HttpStreamParser parser(
243 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog());
[email protected]4750937f2012-06-15 20:44:21244
[email protected]4750937f2012-06-15 20:44:21245 HttpRequestHeaders request_headers;
246 request_headers.SetHeader("Host", "localhost");
247 request_headers.SetHeader("Transfer-Encoding", "chunked");
248 request_headers.SetHeader("Connection", "keep-alive");
249
250 HttpResponseInfo response_info;
251 // This will attempt to Write() the initial request and headers, which will
252 // complete asynchronously.
253 rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
[email protected]bf3eb002012-11-15 05:50:11254 &response_info, callback.callback());
[email protected]4750937f2012-06-15 20:44:21255 ASSERT_EQ(ERR_IO_PENDING, rv);
256
257 // Complete the initial request write. Additionally, this should enqueue the
258 // first chunk.
[email protected]dd54bd82012-07-19 23:44:57259 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21260 ASSERT_FALSE(callback.have_result());
261
262 // Now append another chunk (while the first write is still pending), which
263 // should not confuse the state machine.
[email protected]b2d26cfd2012-12-11 10:36:06264 upload_stream.AppendChunk(kChunk2, arraysize(kChunk2) - 1, false);
[email protected]4750937f2012-06-15 20:44:21265 ASSERT_FALSE(callback.have_result());
266
267 // Complete writing the first chunk, which should then enqueue the second
268 // chunk for writing and return, because it is set to complete
269 // asynchronously.
[email protected]dd54bd82012-07-19 23:44:57270 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21271 ASSERT_FALSE(callback.have_result());
272
273 // Complete writing the second chunk. However, because no chunks are
274 // available yet, no further writes should be called until a new chunk is
275 // added.
[email protected]dd54bd82012-07-19 23:44:57276 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21277 ASSERT_FALSE(callback.have_result());
278
279 // Add the final chunk. This will enqueue another write, but it will not
280 // complete due to the async nature.
[email protected]b2d26cfd2012-12-11 10:36:06281 upload_stream.AppendChunk(kChunk3, arraysize(kChunk3) - 1, true);
[email protected]4750937f2012-06-15 20:44:21282 ASSERT_FALSE(callback.have_result());
283
284 // Finalize writing the last chunk, which will enqueue the trailer.
[email protected]dd54bd82012-07-19 23:44:57285 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21286 ASSERT_FALSE(callback.have_result());
287
288 // Finalize writing the trailer.
[email protected]dd54bd82012-07-19 23:44:57289 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21290 ASSERT_TRUE(callback.have_result());
291
292 // Warning: This will hang if the callback doesn't already have a result,
293 // due to the deterministic socket provider. Do not remove the above
294 // ASSERT_TRUE, which will avoid this hang.
295 rv = callback.WaitForResult();
296 ASSERT_EQ(OK, rv);
297
298 // Attempt to read the response status and the response headers.
299 rv = parser.ReadResponseHeaders(callback.callback());
300 ASSERT_EQ(ERR_IO_PENDING, rv);
[email protected]dd54bd82012-07-19 23:44:57301 data.RunFor(2);
[email protected]4750937f2012-06-15 20:44:21302
303 ASSERT_TRUE(callback.have_result());
304 rv = callback.WaitForResult();
305 ASSERT_GT(rv, 0);
306
307 // Finally, attempt to read the response body.
308 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
[email protected]90499482013-06-01 00:39:50309 rv = parser.ReadResponseBody(
310 body_buffer.get(), kBodySize, callback.callback());
[email protected]4750937f2012-06-15 20:44:21311 ASSERT_EQ(ERR_IO_PENDING, rv);
[email protected]dd54bd82012-07-19 23:44:57312 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21313
314 ASSERT_TRUE(callback.have_result());
315 rv = callback.WaitForResult();
316 ASSERT_EQ(kBodySize, rv);
317}
318
[email protected]9c18dbc2013-05-29 19:06:53319TEST(HttpStreamParser, TruncatedHeaders) {
320 MockRead truncated_status_reads[] = {
321 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 20"),
322 MockRead(SYNCHRONOUS, 0, 2), // EOF
323 };
324
325 MockRead truncated_after_status_reads[] = {
326 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\n"),
327 MockRead(SYNCHRONOUS, 0, 2), // EOF
328 };
329
330 MockRead truncated_in_header_reads[] = {
331 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHead"),
332 MockRead(SYNCHRONOUS, 0, 2), // EOF
333 };
334
335 MockRead truncated_after_header_reads[] = {
336 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n"),
337 MockRead(SYNCHRONOUS, 0, 2), // EOF
338 };
339
340 MockRead truncated_after_final_newline_reads[] = {
341 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r"),
342 MockRead(SYNCHRONOUS, 0, 2), // EOF
343 };
344
345 MockRead not_truncated_reads[] = {
346 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r\n"),
347 MockRead(SYNCHRONOUS, 0, 2), // EOF
348 };
349
350 MockRead* reads[] = {
351 truncated_status_reads,
352 truncated_after_status_reads,
353 truncated_in_header_reads,
354 truncated_after_header_reads,
355 truncated_after_final_newline_reads,
356 not_truncated_reads,
357 };
358
359 MockWrite writes[] = {
360 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n\r\n"),
361 };
362
363 enum {
364 HTTP = 0,
365 HTTPS,
366 NUM_PROTOCOLS,
367 };
368
369 for (size_t protocol = 0; protocol < NUM_PROTOCOLS; protocol++) {
370 SCOPED_TRACE(protocol);
371
372 for (size_t i = 0; i < arraysize(reads); i++) {
373 SCOPED_TRACE(i);
374 DeterministicSocketData data(reads[i], 2, writes, arraysize(writes));
375 data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
376 data.SetStop(3);
377
378 scoped_ptr<DeterministicMockTCPClientSocket> transport(
379 new DeterministicMockTCPClientSocket(NULL, &data));
380 data.set_delegate(transport->AsWeakPtr());
381
382 TestCompletionCallback callback;
383 int rv = transport->Connect(callback.callback());
384 rv = callback.GetResult(rv);
385 ASSERT_EQ(OK, rv);
386
387 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
[email protected]18ccfdb2013-08-15 00:13:44388 socket_handle->SetSocket(transport.PassAs<StreamSocket>());
[email protected]9c18dbc2013-05-29 19:06:53389
390 HttpRequestInfo request_info;
391 request_info.method = "GET";
392 if (protocol == HTTP) {
393 request_info.url = GURL("http://localhost");
394 } else {
395 request_info.url = GURL("https://localhost");
396 }
397 request_info.load_flags = LOAD_NORMAL;
398
399 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
[email protected]90499482013-06-01 00:39:50400 HttpStreamParser parser(
401 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog());
[email protected]9c18dbc2013-05-29 19:06:53402
403 HttpRequestHeaders request_headers;
404 HttpResponseInfo response_info;
405 rv = parser.SendRequest("GET / HTTP/1.1\r\n", request_headers,
406 &response_info, callback.callback());
407 ASSERT_EQ(OK, rv);
408
409 rv = parser.ReadResponseHeaders(callback.callback());
410 if (i == arraysize(reads) - 1) {
411 EXPECT_EQ(OK, rv);
412 EXPECT_TRUE(response_info.headers.get());
413 } else {
414 if (protocol == HTTP) {
415 EXPECT_EQ(ERR_CONNECTION_CLOSED, rv);
416 EXPECT_TRUE(response_info.headers.get());
417 } else {
[email protected]6a621f52013-06-12 19:43:00418 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED, rv);
[email protected]9c18dbc2013-05-29 19:06:53419 EXPECT_FALSE(response_info.headers.get());
420 }
421 }
422 }
423 }
424}
425
[email protected]df8e6382013-11-07 00:19:06426// Confirm that on 101 response, the headers are parsed but the data that
427// follows remains in the buffer.
428TEST(HttpStreamParser, Websocket101Response) {
429 MockRead reads[] = {
430 MockRead(SYNCHRONOUS, 1,
431 "HTTP/1.1 101 Switching Protocols\r\n"
432 "Upgrade: websocket\r\n"
433 "Connection: Upgrade\r\n"
434 "\r\n"
435 "a fake websocket frame"),
436 };
437
438 MockWrite writes[] = {
439 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n\r\n"),
440 };
441
442 DeterministicSocketData data(reads, arraysize(reads),
443 writes, arraysize(writes));
444 data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
445 data.SetStop(2);
446
447 scoped_ptr<DeterministicMockTCPClientSocket> transport(
448 new DeterministicMockTCPClientSocket(NULL, &data));
449 data.set_delegate(transport->AsWeakPtr());
450
451 TestCompletionCallback callback;
452 int rv = transport->Connect(callback.callback());
453 rv = callback.GetResult(rv);
454 ASSERT_EQ(OK, rv);
455
456 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
457 socket_handle->SetSocket(transport.PassAs<StreamSocket>());
458
459 HttpRequestInfo request_info;
460 request_info.method = "GET";
461 request_info.url = GURL("http://localhost");
462 request_info.load_flags = LOAD_NORMAL;
463
464 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
465 HttpStreamParser parser(
466 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog());
467
468 HttpRequestHeaders request_headers;
469 HttpResponseInfo response_info;
470 rv = parser.SendRequest("GET / HTTP/1.1\r\n", request_headers,
471 &response_info, callback.callback());
472 ASSERT_EQ(OK, rv);
473
474 rv = parser.ReadResponseHeaders(callback.callback());
475 EXPECT_EQ(OK, rv);
476 ASSERT_TRUE(response_info.headers.get());
477 EXPECT_EQ(101, response_info.headers->response_code());
478 EXPECT_TRUE(response_info.headers->HasHeaderValue("Connection", "Upgrade"));
479 EXPECT_TRUE(response_info.headers->HasHeaderValue("Upgrade", "websocket"));
480 EXPECT_EQ(read_buffer->capacity(), read_buffer->offset());
481 EXPECT_EQ("a fake websocket frame",
482 base::StringPiece(read_buffer->StartOfBuffer(),
483 read_buffer->capacity()));
484}
485
486
[email protected]6db833d12012-01-21 00:45:19487} // namespace net