Rename InputLatency::ScrollUpdate to Latency::ScrollUpdate
[chromium-blink-merge.git] / net / spdy / spdy_http_stream_unittest.cc
blob6fecf4570d2b3f166a1557fbc6deaa6cea1698a3
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/spdy/spdy_http_stream.h"
7 #include <vector>
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/run_loop.h"
12 #include "base/stl_util.h"
13 #include "crypto/ec_private_key.h"
14 #include "crypto/ec_signature_creator.h"
15 #include "crypto/signature_creator.h"
16 #include "net/base/chunked_upload_data_stream.h"
17 #include "net/base/load_timing_info.h"
18 #include "net/base/load_timing_info_test_util.h"
19 #include "net/base/test_completion_callback.h"
20 #include "net/cert/asn1_util.h"
21 #include "net/http/http_request_info.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/http/http_response_info.h"
24 #include "net/log/test_net_log.h"
25 #include "net/socket/next_proto.h"
26 #include "net/socket/socket_test_util.h"
27 #include "net/spdy/spdy_http_utils.h"
28 #include "net/spdy/spdy_session.h"
29 #include "net/spdy/spdy_test_util_common.h"
30 #include "net/ssl/default_channel_id_store.h"
31 #include "testing/gtest/include/gtest/gtest.h"
33 namespace net {
35 namespace {
37 // Tests the load timing of a stream that's connected and is not the first
38 // request sent on a connection.
39 void TestLoadTimingReused(const HttpStream& stream) {
40 LoadTimingInfo load_timing_info;
41 EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info));
43 EXPECT_TRUE(load_timing_info.socket_reused);
44 EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id);
46 ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
47 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
50 // Tests the load timing of a stream that's connected and using a fresh
51 // connection.
52 void TestLoadTimingNotReused(const HttpStream& stream) {
53 LoadTimingInfo load_timing_info;
54 EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info));
56 EXPECT_FALSE(load_timing_info.socket_reused);
57 EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id);
59 ExpectConnectTimingHasTimes(load_timing_info.connect_timing,
60 CONNECT_TIMING_HAS_DNS_TIMES);
61 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
64 } // namespace
66 class SpdyHttpStreamTest : public testing::Test,
67 public testing::WithParamInterface<NextProto> {
68 public:
69 SpdyHttpStreamTest()
70 : spdy_util_(GetParam()),
71 session_deps_(GetParam()) {
72 session_deps_.net_log = &net_log_;
75 DeterministicSocketData* deterministic_data() {
76 return deterministic_data_.get();
79 protected:
80 void TearDown() override {
81 crypto::ECSignatureCreator::SetFactoryForTesting(NULL);
82 base::MessageLoop::current()->RunUntilIdle();
85 // Initializes the session using DeterministicSocketData.
86 void InitSession(MockRead* reads,
87 size_t reads_count,
88 MockWrite* writes,
89 size_t writes_count,
90 const SpdySessionKey& key) {
91 deterministic_data_.reset(
92 new DeterministicSocketData(reads, reads_count, writes, writes_count));
93 session_deps_.deterministic_socket_factory->AddSocketDataProvider(
94 deterministic_data_.get());
95 http_session_ =
96 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_);
97 session_ = CreateInsecureSpdySession(http_session_, key, BoundNetLog());
100 void TestSendCredentials(
101 ChannelIDService* channel_id_service,
102 const std::string& cert,
103 const std::string& proof);
105 SpdyTestUtil spdy_util_;
106 TestNetLog net_log_;
107 SpdySessionDependencies session_deps_;
108 scoped_ptr<DeterministicSocketData> deterministic_data_;
109 scoped_refptr<HttpNetworkSession> http_session_;
110 base::WeakPtr<SpdySession> session_;
112 private:
113 MockECSignatureCreatorFactory ec_signature_creator_factory_;
116 INSTANTIATE_TEST_CASE_P(NextProto,
117 SpdyHttpStreamTest,
118 testing::Values(kProtoSPDY31,
119 kProtoSPDY4_14,
120 kProtoSPDY4));
122 // SpdyHttpStream::GetUploadProgress() should still work even before the
123 // stream is initialized.
124 TEST_P(SpdyHttpStreamTest, GetUploadProgressBeforeInitialization) {
125 MockRead reads[] = {
126 MockRead(ASYNC, 0, 0) // EOF
129 HostPortPair host_port_pair("www.example.org", 80);
130 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
131 PRIVACY_MODE_DISABLED);
132 InitSession(reads, arraysize(reads), NULL, 0, key);
134 SpdyHttpStream stream(session_, false);
135 UploadProgress progress = stream.GetUploadProgress();
136 EXPECT_EQ(0u, progress.size());
137 EXPECT_EQ(0u, progress.position());
139 // Pump the event loop so |reads| is consumed before the function returns.
140 base::RunLoop().RunUntilIdle();
143 TEST_P(SpdyHttpStreamTest, SendRequest) {
144 scoped_ptr<SpdyFrame> req(
145 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
146 MockWrite writes[] = {
147 CreateMockWrite(*req.get(), 0),
149 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
150 MockRead reads[] = {
151 CreateMockRead(*resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF
154 HostPortPair host_port_pair("www.example.org", 80);
155 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
156 PRIVACY_MODE_DISABLED);
157 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
159 HttpRequestInfo request;
160 request.method = "GET";
161 request.url = GURL("http://www.example.org/");
162 TestCompletionCallback callback;
163 HttpResponseInfo response;
164 HttpRequestHeaders headers;
165 BoundNetLog net_log;
166 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
167 // Make sure getting load timing information the stream early does not crash.
168 LoadTimingInfo load_timing_info;
169 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
171 ASSERT_EQ(
173 http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
174 net_log, CompletionCallback()));
175 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
177 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
178 callback.callback()));
179 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
180 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
182 deterministic_data()->RunFor(3);
184 callback.WaitForResult();
186 // Can get timing information once the stream connects.
187 TestLoadTimingNotReused(*http_stream);
189 // Because we abandoned the stream, we don't expect to find a session in the
190 // pool anymore.
191 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
192 EXPECT_TRUE(deterministic_data()->at_read_eof());
193 EXPECT_TRUE(deterministic_data()->at_write_eof());
195 TestLoadTimingNotReused(*http_stream);
196 http_stream->Close(true);
197 // Test that there's no crash when trying to get the load timing after the
198 // stream has been closed.
199 TestLoadTimingNotReused(*http_stream);
202 TEST_P(SpdyHttpStreamTest, LoadTimingTwoRequests) {
203 scoped_ptr<SpdyFrame> req1(
204 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
205 scoped_ptr<SpdyFrame> req2(
206 spdy_util_.ConstructSpdyGet(NULL, 0, false, 3, LOWEST, true));
207 MockWrite writes[] = {
208 CreateMockWrite(*req1, 0),
209 CreateMockWrite(*req2, 1),
211 scoped_ptr<SpdyFrame> resp1(
212 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
213 scoped_ptr<SpdyFrame> body1(
214 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
215 scoped_ptr<SpdyFrame> resp2(
216 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
217 scoped_ptr<SpdyFrame> body2(
218 spdy_util_.ConstructSpdyBodyFrame(3, "", 0, true));
219 MockRead reads[] = {
220 CreateMockRead(*resp1, 2),
221 CreateMockRead(*body1, 3),
222 CreateMockRead(*resp2, 4),
223 CreateMockRead(*body2, 5),
224 MockRead(ASYNC, 0, 6) // EOF
227 HostPortPair host_port_pair("www.example.org", 80);
228 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
229 PRIVACY_MODE_DISABLED);
230 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
232 HttpRequestInfo request1;
233 request1.method = "GET";
234 request1.url = GURL("http://www.example.org/");
235 TestCompletionCallback callback1;
236 HttpResponseInfo response1;
237 HttpRequestHeaders headers1;
238 scoped_ptr<SpdyHttpStream> http_stream1(new SpdyHttpStream(session_, true));
240 HttpRequestInfo request2;
241 request2.method = "GET";
242 request2.url = GURL("http://www.example.org/");
243 TestCompletionCallback callback2;
244 HttpResponseInfo response2;
245 HttpRequestHeaders headers2;
246 scoped_ptr<SpdyHttpStream> http_stream2(new SpdyHttpStream(session_, true));
248 // First write.
249 ASSERT_EQ(OK,
250 http_stream1->InitializeStream(&request1, DEFAULT_PRIORITY,
251 BoundNetLog(),
252 CompletionCallback()));
253 EXPECT_EQ(ERR_IO_PENDING, http_stream1->SendRequest(headers1, &response1,
254 callback1.callback()));
255 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
257 deterministic_data()->RunFor(1);
258 EXPECT_LE(0, callback1.WaitForResult());
260 TestLoadTimingNotReused(*http_stream1);
261 LoadTimingInfo load_timing_info1;
262 LoadTimingInfo load_timing_info2;
263 EXPECT_TRUE(http_stream1->GetLoadTimingInfo(&load_timing_info1));
264 EXPECT_FALSE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
266 // Second write.
267 ASSERT_EQ(OK,
268 http_stream2->InitializeStream(&request2, DEFAULT_PRIORITY,
269 BoundNetLog(),
270 CompletionCallback()));
271 EXPECT_EQ(ERR_IO_PENDING, http_stream2->SendRequest(headers2, &response2,
272 callback2.callback()));
273 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
275 deterministic_data()->RunFor(1);
276 EXPECT_LE(0, callback2.WaitForResult());
277 TestLoadTimingReused(*http_stream2);
278 EXPECT_TRUE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
279 EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id);
281 // All the reads.
282 deterministic_data()->RunFor(6);
284 // Read stream 1 to completion, before making sure we can still read load
285 // timing from both streams.
286 scoped_refptr<IOBuffer> buf1(new IOBuffer(1));
287 ASSERT_EQ(
288 0, http_stream1->ReadResponseBody(buf1.get(), 1, callback1.callback()));
290 // Stream 1 has been read to completion.
291 TestLoadTimingNotReused(*http_stream1);
292 // Stream 2 still has queued body data.
293 TestLoadTimingReused(*http_stream2);
296 TEST_P(SpdyHttpStreamTest, SendChunkedPost) {
297 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
299 scoped_ptr<SpdyFrame> req(
300 spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
301 scoped_ptr<SpdyFrame> body(
302 framer.CreateDataFrame(1, kUploadData, kUploadDataSize, DATA_FLAG_FIN));
303 std::vector<MockWrite> writes;
304 int seq = 0;
305 writes.push_back(CreateMockWrite(*req, seq++));
306 writes.push_back(CreateMockWrite(*body, seq++)); // POST upload frame
308 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
309 std::vector<MockRead> reads;
310 reads.push_back(CreateMockRead(*resp, seq++));
311 reads.push_back(CreateMockRead(*body, seq++));
312 reads.push_back(MockRead(SYNCHRONOUS, 0, seq++)); // EOF
314 HostPortPair host_port_pair("www.example.org", 80);
315 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
316 PRIVACY_MODE_DISABLED);
317 InitSession(vector_as_array(&reads), reads.size(), vector_as_array(&writes),
318 writes.size(), key);
319 EXPECT_EQ(spdy_util_.spdy_version(), session_->GetProtocolVersion());
321 ChunkedUploadDataStream upload_stream(0);
322 const int kFirstChunkSize = kUploadDataSize/2;
323 upload_stream.AppendData(kUploadData, kFirstChunkSize, false);
324 upload_stream.AppendData(kUploadData + kFirstChunkSize,
325 kUploadDataSize - kFirstChunkSize, true);
327 HttpRequestInfo request;
328 request.method = "POST";
329 request.url = GURL("http://www.example.org/");
330 request.upload_data_stream = &upload_stream;
332 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
334 TestCompletionCallback callback;
335 HttpResponseInfo response;
336 HttpRequestHeaders headers;
337 BoundNetLog net_log;
338 SpdyHttpStream http_stream(session_, true);
339 ASSERT_EQ(
341 http_stream.InitializeStream(&request, DEFAULT_PRIORITY,
342 net_log, CompletionCallback()));
344 EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest(
345 headers, &response, callback.callback()));
346 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
348 deterministic_data()->RunFor(seq);
349 callback.WaitForResult();
351 // Because we abandoned the stream, we don't expect to find a session in the
352 // pool anymore.
353 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
354 EXPECT_TRUE(deterministic_data()->at_read_eof());
355 EXPECT_TRUE(deterministic_data()->at_write_eof());
358 // Test to ensure the SpdyStream state machine does not get confused when a
359 // chunk becomes available while a write is pending.
360 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPost) {
361 const char kUploadData1[] = "12345678";
362 const int kUploadData1Size = arraysize(kUploadData1)-1;
363 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
364 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false));
365 scoped_ptr<SpdyFrame> chunk2(
366 spdy_util_.ConstructSpdyBodyFrame(
367 1, kUploadData1, kUploadData1Size, false));
368 scoped_ptr<SpdyFrame> chunk3(spdy_util_.ConstructSpdyBodyFrame(1, true));
369 MockWrite writes[] = {
370 CreateMockWrite(*req.get(), 0),
371 CreateMockWrite(*chunk1, 1), // POST upload frames
372 CreateMockWrite(*chunk2, 2),
373 CreateMockWrite(*chunk3, 3),
375 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
376 MockRead reads[] = {
377 CreateMockRead(*resp, 4),
378 CreateMockRead(*chunk1, 5),
379 CreateMockRead(*chunk2, 6),
380 CreateMockRead(*chunk3, 7),
381 MockRead(ASYNC, 0, 8) // EOF
384 HostPortPair host_port_pair("www.example.org", 80);
385 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
386 PRIVACY_MODE_DISABLED);
387 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
389 ChunkedUploadDataStream upload_stream(0);
391 HttpRequestInfo request;
392 request.method = "POST";
393 request.url = GURL("http://www.example.org/");
394 request.upload_data_stream = &upload_stream;
396 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
397 upload_stream.AppendData(kUploadData, kUploadDataSize, false);
399 BoundNetLog net_log;
400 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
401 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
402 net_log, CompletionCallback()));
404 TestCompletionCallback callback;
405 HttpRequestHeaders headers;
406 HttpResponseInfo response;
407 // This will attempt to Write() the initial request and headers, which will
408 // complete asynchronously.
409 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
410 callback.callback()));
411 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
413 // Complete the initial request write and the first chunk.
414 deterministic_data()->RunFor(2);
415 ASSERT_TRUE(callback.have_result());
416 EXPECT_EQ(OK, callback.WaitForResult());
418 // Now append the final two chunks which will enqueue two more writes.
419 upload_stream.AppendData(kUploadData1, kUploadData1Size, false);
420 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
422 // Finish writing all the chunks.
423 deterministic_data()->RunFor(2);
425 // Read response headers.
426 deterministic_data()->RunFor(1);
427 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
429 // Read and check |chunk1| response.
430 deterministic_data()->RunFor(1);
431 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
432 ASSERT_EQ(kUploadDataSize,
433 http_stream->ReadResponseBody(
434 buf1.get(), kUploadDataSize, callback.callback()));
435 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
437 // Read and check |chunk2| response.
438 deterministic_data()->RunFor(1);
439 scoped_refptr<IOBuffer> buf2(new IOBuffer(kUploadData1Size));
440 ASSERT_EQ(kUploadData1Size,
441 http_stream->ReadResponseBody(
442 buf2.get(), kUploadData1Size, callback.callback()));
443 EXPECT_EQ(kUploadData1, std::string(buf2->data(), kUploadData1Size));
445 // Read and check |chunk3| response.
446 deterministic_data()->RunFor(1);
447 scoped_refptr<IOBuffer> buf3(new IOBuffer(kUploadDataSize));
448 ASSERT_EQ(kUploadDataSize,
449 http_stream->ReadResponseBody(
450 buf3.get(), kUploadDataSize, callback.callback()));
451 EXPECT_EQ(kUploadData, std::string(buf3->data(), kUploadDataSize));
453 // Finish reading the |EOF|.
454 deterministic_data()->RunFor(1);
455 ASSERT_TRUE(response.headers.get());
456 ASSERT_EQ(200, response.headers->response_code());
457 EXPECT_TRUE(deterministic_data()->at_read_eof());
458 EXPECT_TRUE(deterministic_data()->at_write_eof());
461 // Test that the SpdyStream state machine can handle sending a final empty data
462 // frame when uploading a chunked data stream.
463 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithEmptyFinalDataFrame) {
464 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
465 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false));
466 scoped_ptr<SpdyFrame> chunk2(
467 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
468 MockWrite writes[] = {
469 CreateMockWrite(*req.get(), 0),
470 CreateMockWrite(*chunk1, 1), // POST upload frames
471 CreateMockWrite(*chunk2, 2),
473 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
474 MockRead reads[] = {
475 CreateMockRead(*resp, 3),
476 CreateMockRead(*chunk1, 4),
477 CreateMockRead(*chunk2, 5),
478 MockRead(ASYNC, 0, 6) // EOF
481 HostPortPair host_port_pair("www.example.org", 80);
482 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
483 PRIVACY_MODE_DISABLED);
484 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
486 ChunkedUploadDataStream upload_stream(0);
488 HttpRequestInfo request;
489 request.method = "POST";
490 request.url = GURL("http://www.example.org/");
491 request.upload_data_stream = &upload_stream;
493 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
494 upload_stream.AppendData(kUploadData, kUploadDataSize, false);
496 BoundNetLog net_log;
497 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
498 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
499 net_log, CompletionCallback()));
501 TestCompletionCallback callback;
502 HttpRequestHeaders headers;
503 HttpResponseInfo response;
504 // This will attempt to Write() the initial request and headers, which will
505 // complete asynchronously.
506 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
507 callback.callback()));
508 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
510 // Complete the initial request write and the first chunk.
511 deterministic_data()->RunFor(2);
512 ASSERT_TRUE(callback.have_result());
513 EXPECT_EQ(OK, callback.WaitForResult());
515 // Now end the stream with an empty data frame and the FIN set.
516 upload_stream.AppendData(NULL, 0, true);
518 // Finish writing the final frame.
519 deterministic_data()->RunFor(1);
521 // Read response headers.
522 deterministic_data()->RunFor(1);
523 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
525 // Read and check |chunk1| response.
526 deterministic_data()->RunFor(1);
527 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
528 ASSERT_EQ(kUploadDataSize,
529 http_stream->ReadResponseBody(
530 buf1.get(), kUploadDataSize, callback.callback()));
531 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
533 // Read and check |chunk2| response.
534 deterministic_data()->RunFor(1);
535 ASSERT_EQ(0,
536 http_stream->ReadResponseBody(
537 buf1.get(), kUploadDataSize, callback.callback()));
539 // Finish reading the |EOF|.
540 deterministic_data()->RunFor(1);
541 ASSERT_TRUE(response.headers.get());
542 ASSERT_EQ(200, response.headers->response_code());
543 EXPECT_TRUE(deterministic_data()->at_read_eof());
544 EXPECT_TRUE(deterministic_data()->at_write_eof());
547 // Test that the SpdyStream state machine handles a chunked upload with no
548 // payload. Unclear if this is a case worth supporting.
549 TEST_P(SpdyHttpStreamTest, ChunkedPostWithEmptyPayload) {
550 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
551 scoped_ptr<SpdyFrame> chunk(
552 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
553 MockWrite writes[] = {
554 CreateMockWrite(*req.get(), 0),
555 CreateMockWrite(*chunk, 1),
557 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
558 MockRead reads[] = {
559 CreateMockRead(*resp, 2),
560 CreateMockRead(*chunk, 3),
561 MockRead(ASYNC, 0, 4) // EOF
564 HostPortPair host_port_pair("www.example.org", 80);
565 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
566 PRIVACY_MODE_DISABLED);
567 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
569 ChunkedUploadDataStream upload_stream(0);
571 HttpRequestInfo request;
572 request.method = "POST";
573 request.url = GURL("http://www.example.org/");
574 request.upload_data_stream = &upload_stream;
576 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
577 upload_stream.AppendData("", 0, true);
579 BoundNetLog net_log;
580 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
581 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
582 net_log, CompletionCallback()));
584 TestCompletionCallback callback;
585 HttpRequestHeaders headers;
586 HttpResponseInfo response;
587 // This will attempt to Write() the initial request and headers, which will
588 // complete asynchronously.
589 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
590 callback.callback()));
591 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
593 // Complete writing request, followed by a FIN.
594 deterministic_data()->RunFor(2);
595 ASSERT_TRUE(callback.have_result());
596 EXPECT_EQ(OK, callback.WaitForResult());
598 // Read response headers.
599 deterministic_data()->RunFor(1);
600 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
602 // Read and check |chunk| response.
603 deterministic_data()->RunFor(1);
604 scoped_refptr<IOBuffer> buf(new IOBuffer(1));
605 ASSERT_EQ(0,
606 http_stream->ReadResponseBody(
607 buf.get(), 1, callback.callback()));
609 // Finish reading the |EOF|.
610 deterministic_data()->RunFor(1);
611 ASSERT_TRUE(response.headers.get());
612 ASSERT_EQ(200, response.headers->response_code());
613 EXPECT_TRUE(deterministic_data()->at_read_eof());
614 EXPECT_TRUE(deterministic_data()->at_write_eof());
617 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
618 TEST_P(SpdyHttpStreamTest, SpdyURLTest) {
619 const char* const full_url = "http://www.example.org/foo?query=what#anchor";
620 const char* const base_url = "http://www.example.org/foo?query=what";
621 scoped_ptr<SpdyFrame> req(
622 spdy_util_.ConstructSpdyGet(base_url, false, 1, LOWEST));
623 MockWrite writes[] = {
624 CreateMockWrite(*req.get(), 0),
626 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
627 MockRead reads[] = {
628 CreateMockRead(*resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF
631 HostPortPair host_port_pair("www.example.org", 80);
632 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
633 PRIVACY_MODE_DISABLED);
634 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
636 HttpRequestInfo request;
637 request.method = "GET";
638 request.url = GURL(full_url);
639 TestCompletionCallback callback;
640 HttpResponseInfo response;
641 HttpRequestHeaders headers;
642 BoundNetLog net_log;
643 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
644 ASSERT_EQ(OK,
645 http_stream->InitializeStream(
646 &request, DEFAULT_PRIORITY, net_log, CompletionCallback()));
648 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
649 callback.callback()));
651 EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec());
653 deterministic_data()->RunFor(3);
654 callback.WaitForResult();
656 // Because we abandoned the stream, we don't expect to find a session in the
657 // pool anymore.
658 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
659 EXPECT_TRUE(deterministic_data()->at_read_eof());
660 EXPECT_TRUE(deterministic_data()->at_write_eof());
663 // The tests below are only for SPDY/3 and above.
665 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be
666 // made available is handled correctly.
667 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) {
668 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
669 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, true));
670 MockWrite writes[] = {
671 CreateMockWrite(*req.get(), 0),
672 CreateMockWrite(*chunk1, 1),
674 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
675 scoped_ptr<SpdyFrame> window_update(
676 spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize));
677 MockRead reads[] = {
678 CreateMockRead(*window_update, 2),
679 CreateMockRead(*resp, 3),
680 CreateMockRead(*chunk1, 4),
681 MockRead(ASYNC, 0, 5) // EOF
684 HostPortPair host_port_pair("www.example.org", 80);
685 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
686 PRIVACY_MODE_DISABLED);
688 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
690 ChunkedUploadDataStream upload_stream(0);
692 HttpRequestInfo request;
693 request.method = "POST";
694 request.url = GURL("http://www.example.org/");
695 request.upload_data_stream = &upload_stream;
697 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
698 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
700 BoundNetLog net_log;
701 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
702 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
703 net_log, CompletionCallback()));
705 HttpRequestHeaders headers;
706 HttpResponseInfo response;
707 // This will attempt to Write() the initial request and headers, which will
708 // complete asynchronously.
709 TestCompletionCallback callback;
710 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
711 callback.callback()));
712 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
714 // Complete the initial request write and first chunk.
715 deterministic_data_->RunFor(2);
716 ASSERT_TRUE(callback.have_result());
717 EXPECT_EQ(OK, callback.WaitForResult());
719 // Verify that the window size has decreased.
720 ASSERT_TRUE(http_stream->stream() != NULL);
721 EXPECT_NE(static_cast<int>(
722 SpdySession::GetDefaultInitialWindowSize(session_->protocol())),
723 http_stream->stream()->send_window_size());
725 // Read window update.
726 deterministic_data_->RunFor(1);
728 // Verify the window update.
729 ASSERT_TRUE(http_stream->stream() != NULL);
730 EXPECT_EQ(static_cast<int>(
731 SpdySession::GetDefaultInitialWindowSize(session_->protocol())),
732 http_stream->stream()->send_window_size());
734 // Read response headers.
735 deterministic_data_->RunFor(1);
736 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
738 // Read and check |chunk1| response.
739 deterministic_data_->RunFor(1);
740 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
741 ASSERT_EQ(kUploadDataSize,
742 http_stream->ReadResponseBody(
743 buf1.get(), kUploadDataSize, callback.callback()));
744 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
746 // Finish reading the |EOF|.
747 deterministic_data_->RunFor(1);
748 ASSERT_TRUE(response.headers.get());
749 ASSERT_EQ(200, response.headers->response_code());
750 EXPECT_TRUE(deterministic_data_->at_read_eof());
751 EXPECT_TRUE(deterministic_data_->at_write_eof());
754 // TODO(willchan): Write a longer test for SpdyStream that exercises all
755 // methods.
757 } // namespace net