Fix infinite recursion on hiding panel when created during fullscreen mode.
[chromium-blink-merge.git] / net / spdy / spdy_http_stream_unittest.cc
blob2142d586eb18f690378fb35e1fb4674a6b80d656
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/capturing_net_log.h"
17 #include "net/base/load_timing_info.h"
18 #include "net/base/load_timing_info_test_util.h"
19 #include "net/base/upload_data_stream.h"
20 #include "net/base/upload_element_reader.h"
21 #include "net/cert/asn1_util.h"
22 #include "net/http/http_request_info.h"
23 #include "net/http/http_response_headers.h"
24 #include "net/http/http_response_info.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_server_bound_cert_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 OrderedSocketData* data() { return data_.get(); }
81 protected:
82 virtual void TearDown() OVERRIDE {
83 crypto::ECSignatureCreator::SetFactoryForTesting(NULL);
84 base::MessageLoop::current()->RunUntilIdle();
87 // Initializes the session using DeterministicSocketData. It's advisable
88 // to use this function rather than the OrderedSocketData, since the
89 // DeterministicSocketData behaves in a reasonable manner.
90 void InitSessionDeterministic(MockRead* reads, size_t reads_count,
91 MockWrite* writes, size_t writes_count,
92 const SpdySessionKey& key) {
93 deterministic_data_.reset(
94 new DeterministicSocketData(reads, reads_count, writes, writes_count));
95 session_deps_.deterministic_socket_factory->AddSocketDataProvider(
96 deterministic_data_.get());
97 http_session_ =
98 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_);
99 session_ = CreateInsecureSpdySession(http_session_, key, BoundNetLog());
102 // Initializes the session using the finicky OrderedSocketData class.
103 void InitSession(MockRead* reads, size_t reads_count,
104 MockWrite* writes, size_t writes_count,
105 const SpdySessionKey& key) {
106 data_.reset(new OrderedSocketData(reads, reads_count,
107 writes, writes_count));
108 session_deps_.socket_factory->AddSocketDataProvider(data_.get());
109 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
110 session_ = CreateInsecureSpdySession(http_session_, key, BoundNetLog());
113 void TestSendCredentials(
114 ServerBoundCertService* server_bound_cert_service,
115 const std::string& cert,
116 const std::string& proof);
118 SpdyTestUtil spdy_util_;
119 CapturingNetLog net_log_;
120 SpdySessionDependencies session_deps_;
121 scoped_ptr<OrderedSocketData> data_;
122 scoped_ptr<DeterministicSocketData> deterministic_data_;
123 scoped_refptr<HttpNetworkSession> http_session_;
124 base::WeakPtr<SpdySession> session_;
126 private:
127 MockECSignatureCreatorFactory ec_signature_creator_factory_;
130 INSTANTIATE_TEST_CASE_P(
131 NextProto,
132 SpdyHttpStreamTest,
133 testing::Values(kProtoDeprecatedSPDY2,
134 kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
135 kProtoHTTP2Draft04));
137 // SpdyHttpStream::GetUploadProgress() should still work even before the
138 // stream is initialized.
139 TEST_P(SpdyHttpStreamTest, GetUploadProgressBeforeInitialization) {
140 MockRead reads[] = {
141 MockRead(ASYNC, 0, 0) // EOF
144 HostPortPair host_port_pair("www.google.com", 80);
145 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
146 kPrivacyModeDisabled);
147 InitSession(reads, arraysize(reads), NULL, 0, key);
149 SpdyHttpStream stream(session_, false);
150 UploadProgress progress = stream.GetUploadProgress();
151 EXPECT_EQ(0u, progress.size());
152 EXPECT_EQ(0u, progress.position());
154 // Pump the event loop so |reads| is consumed before the function returns.
155 base::RunLoop().RunUntilIdle();
158 TEST_P(SpdyHttpStreamTest, SendRequest) {
159 scoped_ptr<SpdyFrame> req(
160 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
161 MockWrite writes[] = {
162 CreateMockWrite(*req.get(), 1),
164 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
165 MockRead reads[] = {
166 CreateMockRead(*resp, 2),
167 MockRead(SYNCHRONOUS, 0, 3) // EOF
170 HostPortPair host_port_pair("www.google.com", 80);
171 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
172 kPrivacyModeDisabled);
173 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
175 HttpRequestInfo request;
176 request.method = "GET";
177 request.url = GURL("http://www.google.com/");
178 TestCompletionCallback callback;
179 HttpResponseInfo response;
180 HttpRequestHeaders headers;
181 BoundNetLog net_log;
182 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
183 // Make sure getting load timing information the stream early does not crash.
184 LoadTimingInfo load_timing_info;
185 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
187 ASSERT_EQ(
189 http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
190 net_log, CompletionCallback()));
191 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
193 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
194 callback.callback()));
195 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
196 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
198 // This triggers the MockWrite and read 2
199 callback.WaitForResult();
201 // Can get timing information once the stream connects.
202 TestLoadTimingNotReused(*http_stream);
204 // This triggers read 3. The empty read causes the session to shut down.
205 data()->CompleteRead();
207 // Because we abandoned the stream, we don't expect to find a session in the
208 // pool anymore.
209 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
210 EXPECT_TRUE(data()->at_read_eof());
211 EXPECT_TRUE(data()->at_write_eof());
213 TestLoadTimingNotReused(*http_stream);
214 http_stream->Close(true);
215 // Test that there's no crash when trying to get the load timing after the
216 // stream has been closed.
217 TestLoadTimingNotReused(*http_stream);
220 TEST_P(SpdyHttpStreamTest, LoadTimingTwoRequests) {
221 scoped_ptr<SpdyFrame> req1(
222 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
223 scoped_ptr<SpdyFrame> req2(
224 spdy_util_.ConstructSpdyGet(NULL, 0, false, 3, LOWEST, true));
225 MockWrite writes[] = {
226 CreateMockWrite(*req1, 0),
227 CreateMockWrite(*req2, 1),
229 scoped_ptr<SpdyFrame> resp1(
230 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
231 scoped_ptr<SpdyFrame> body1(
232 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
233 scoped_ptr<SpdyFrame> resp2(
234 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
235 scoped_ptr<SpdyFrame> body2(
236 spdy_util_.ConstructSpdyBodyFrame(3, "", 0, true));
237 MockRead reads[] = {
238 CreateMockRead(*resp1, 2),
239 CreateMockRead(*body1, 3),
240 CreateMockRead(*resp2, 4),
241 CreateMockRead(*body2, 5),
242 MockRead(ASYNC, 0, 6) // EOF
245 HostPortPair host_port_pair("www.google.com", 80);
246 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
247 kPrivacyModeDisabled);
248 InitSessionDeterministic(reads, arraysize(reads),
249 writes, arraysize(writes),
250 key);
252 HttpRequestInfo request1;
253 request1.method = "GET";
254 request1.url = GURL("http://www.google.com/");
255 TestCompletionCallback callback1;
256 HttpResponseInfo response1;
257 HttpRequestHeaders headers1;
258 scoped_ptr<SpdyHttpStream> http_stream1(new SpdyHttpStream(session_, true));
260 HttpRequestInfo request2;
261 request2.method = "GET";
262 request2.url = GURL("http://www.google.com/");
263 TestCompletionCallback callback2;
264 HttpResponseInfo response2;
265 HttpRequestHeaders headers2;
266 scoped_ptr<SpdyHttpStream> http_stream2(new SpdyHttpStream(session_, true));
268 // First write.
269 ASSERT_EQ(OK,
270 http_stream1->InitializeStream(&request1, DEFAULT_PRIORITY,
271 BoundNetLog(),
272 CompletionCallback()));
273 EXPECT_EQ(ERR_IO_PENDING, http_stream1->SendRequest(headers1, &response1,
274 callback1.callback()));
275 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
277 deterministic_data()->RunFor(1);
278 EXPECT_LE(0, callback1.WaitForResult());
280 TestLoadTimingNotReused(*http_stream1);
281 LoadTimingInfo load_timing_info1;
282 LoadTimingInfo load_timing_info2;
283 EXPECT_TRUE(http_stream1->GetLoadTimingInfo(&load_timing_info1));
284 EXPECT_FALSE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
286 // Second write.
287 ASSERT_EQ(OK,
288 http_stream2->InitializeStream(&request2, DEFAULT_PRIORITY,
289 BoundNetLog(),
290 CompletionCallback()));
291 EXPECT_EQ(ERR_IO_PENDING, http_stream2->SendRequest(headers2, &response2,
292 callback2.callback()));
293 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
295 deterministic_data()->RunFor(1);
296 EXPECT_LE(0, callback2.WaitForResult());
297 TestLoadTimingReused(*http_stream2);
298 EXPECT_TRUE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
299 EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id);
301 // All the reads.
302 deterministic_data()->RunFor(6);
304 // Read stream 1 to completion, before making sure we can still read load
305 // timing from both streams.
306 scoped_refptr<IOBuffer> buf1(new IOBuffer(1));
307 ASSERT_EQ(
308 0, http_stream1->ReadResponseBody(buf1.get(), 1, callback1.callback()));
310 // Stream 1 has been read to completion.
311 TestLoadTimingNotReused(*http_stream1);
312 // Stream 2 still has queued body data.
313 TestLoadTimingReused(*http_stream2);
316 TEST_P(SpdyHttpStreamTest, SendChunkedPost) {
317 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
319 scoped_ptr<SpdyFrame> req(
320 spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
321 scoped_ptr<SpdyFrame> body(
322 framer.CreateDataFrame(1, kUploadData, kUploadDataSize, DATA_FLAG_FIN));
323 std::vector<MockWrite> writes;
324 int seq = 0;
325 writes.push_back(CreateMockWrite(*req, seq++));
326 writes.push_back(CreateMockWrite(*body, seq++)); // POST upload frame
328 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
329 std::vector<MockRead> reads;
330 reads.push_back(CreateMockRead(*resp, seq++));
331 reads.push_back(CreateMockRead(*body, seq++));
332 reads.push_back(MockRead(SYNCHRONOUS, 0, seq++)); // EOF
334 HostPortPair host_port_pair("www.google.com", 80);
335 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
336 kPrivacyModeDisabled);
337 InitSession(vector_as_array(&reads), reads.size(),
338 vector_as_array(&writes), writes.size(),
339 key);
340 EXPECT_EQ(spdy_util_.spdy_version(), session_->GetProtocolVersion());
342 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0);
343 const int kFirstChunkSize = kUploadDataSize/2;
344 upload_stream.AppendChunk(kUploadData, kFirstChunkSize, false);
345 upload_stream.AppendChunk(kUploadData + kFirstChunkSize,
346 kUploadDataSize - kFirstChunkSize, true);
348 HttpRequestInfo request;
349 request.method = "POST";
350 request.url = GURL("http://www.google.com/");
351 request.upload_data_stream = &upload_stream;
353 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback()));
355 TestCompletionCallback callback;
356 HttpResponseInfo response;
357 HttpRequestHeaders headers;
358 BoundNetLog net_log;
359 SpdyHttpStream http_stream(session_, true);
360 ASSERT_EQ(
362 http_stream.InitializeStream(&request, DEFAULT_PRIORITY,
363 net_log, CompletionCallback()));
365 EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest(
366 headers, &response, callback.callback()));
367 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
369 // This results in writing the post body and reading the response headers.
370 callback.WaitForResult();
372 // This triggers reading the body and the EOF, causing the session to shut
373 // down.
374 data()->CompleteRead();
375 base::MessageLoop::current()->RunUntilIdle();
377 // Because we abandoned the stream, we don't expect to find a session in the
378 // pool anymore.
379 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
380 EXPECT_TRUE(data()->at_read_eof());
381 EXPECT_TRUE(data()->at_write_eof());
384 // Test to ensure the SpdyStream state machine does not get confused when a
385 // chunk becomes available while a write is pending.
386 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPost) {
387 const char kUploadData1[] = "12345678";
388 const int kUploadData1Size = arraysize(kUploadData1)-1;
389 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
390 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false));
391 scoped_ptr<SpdyFrame> chunk2(
392 spdy_util_.ConstructSpdyBodyFrame(
393 1, kUploadData1, kUploadData1Size, false));
394 scoped_ptr<SpdyFrame> chunk3(spdy_util_.ConstructSpdyBodyFrame(1, true));
395 MockWrite writes[] = {
396 CreateMockWrite(*req.get(), 0),
397 CreateMockWrite(*chunk1, 1), // POST upload frames
398 CreateMockWrite(*chunk2, 2),
399 CreateMockWrite(*chunk3, 3),
401 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
402 MockRead reads[] = {
403 CreateMockRead(*resp, 4),
404 CreateMockRead(*chunk1, 5),
405 CreateMockRead(*chunk2, 6),
406 CreateMockRead(*chunk3, 7),
407 MockRead(ASYNC, 0, 8) // EOF
410 HostPortPair host_port_pair("www.google.com", 80);
411 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
412 kPrivacyModeDisabled);
413 InitSessionDeterministic(reads, arraysize(reads),
414 writes, arraysize(writes),
415 key);
417 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0);
419 HttpRequestInfo request;
420 request.method = "POST";
421 request.url = GURL("http://www.google.com/");
422 request.upload_data_stream = &upload_stream;
424 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback()));
425 upload_stream.AppendChunk(kUploadData, kUploadDataSize, false);
427 BoundNetLog net_log;
428 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
429 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
430 net_log, CompletionCallback()));
432 TestCompletionCallback callback;
433 HttpRequestHeaders headers;
434 HttpResponseInfo response;
435 // This will attempt to Write() the initial request and headers, which will
436 // complete asynchronously.
437 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
438 callback.callback()));
439 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
441 // Complete the initial request write and the first chunk.
442 deterministic_data()->RunFor(2);
443 ASSERT_TRUE(callback.have_result());
444 EXPECT_EQ(OK, callback.WaitForResult());
446 // Now append the final two chunks which will enqueue two more writes.
447 upload_stream.AppendChunk(kUploadData1, kUploadData1Size, false);
448 upload_stream.AppendChunk(kUploadData, kUploadDataSize, true);
450 // Finish writing all the chunks.
451 deterministic_data()->RunFor(2);
453 // Read response headers.
454 deterministic_data()->RunFor(1);
455 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
457 // Read and check |chunk1| response.
458 deterministic_data()->RunFor(1);
459 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
460 ASSERT_EQ(kUploadDataSize,
461 http_stream->ReadResponseBody(
462 buf1.get(), kUploadDataSize, callback.callback()));
463 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
465 // Read and check |chunk2| response.
466 deterministic_data()->RunFor(1);
467 scoped_refptr<IOBuffer> buf2(new IOBuffer(kUploadData1Size));
468 ASSERT_EQ(kUploadData1Size,
469 http_stream->ReadResponseBody(
470 buf2.get(), kUploadData1Size, callback.callback()));
471 EXPECT_EQ(kUploadData1, std::string(buf2->data(), kUploadData1Size));
473 // Read and check |chunk3| response.
474 deterministic_data()->RunFor(1);
475 scoped_refptr<IOBuffer> buf3(new IOBuffer(kUploadDataSize));
476 ASSERT_EQ(kUploadDataSize,
477 http_stream->ReadResponseBody(
478 buf3.get(), kUploadDataSize, callback.callback()));
479 EXPECT_EQ(kUploadData, std::string(buf3->data(), kUploadDataSize));
481 // Finish reading the |EOF|.
482 deterministic_data()->RunFor(1);
483 ASSERT_TRUE(response.headers.get());
484 ASSERT_EQ(200, response.headers->response_code());
485 EXPECT_TRUE(deterministic_data()->at_read_eof());
486 EXPECT_TRUE(deterministic_data()->at_write_eof());
489 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
490 TEST_P(SpdyHttpStreamTest, SpdyURLTest) {
491 const char * const full_url = "http://www.google.com/foo?query=what#anchor";
492 const char * const base_url = "http://www.google.com/foo?query=what";
493 scoped_ptr<SpdyFrame> req(
494 spdy_util_.ConstructSpdyGet(base_url, false, 1, LOWEST));
495 MockWrite writes[] = {
496 CreateMockWrite(*req.get(), 1),
498 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
499 MockRead reads[] = {
500 CreateMockRead(*resp, 2),
501 MockRead(SYNCHRONOUS, 0, 3) // EOF
504 HostPortPair host_port_pair("www.google.com", 80);
505 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
506 kPrivacyModeDisabled);
507 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
509 HttpRequestInfo request;
510 request.method = "GET";
511 request.url = GURL(full_url);
512 TestCompletionCallback callback;
513 HttpResponseInfo response;
514 HttpRequestHeaders headers;
515 BoundNetLog net_log;
516 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
517 ASSERT_EQ(OK,
518 http_stream->InitializeStream(
519 &request, DEFAULT_PRIORITY, net_log, CompletionCallback()));
521 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
522 callback.callback()));
524 EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec());
526 // This triggers the MockWrite and read 2
527 callback.WaitForResult();
529 // This triggers read 3. The empty read causes the session to shut down.
530 data()->CompleteRead();
532 // Because we abandoned the stream, we don't expect to find a session in the
533 // pool anymore.
534 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
535 EXPECT_TRUE(data()->at_read_eof());
536 EXPECT_TRUE(data()->at_write_eof());
539 // The tests below are only for SPDY/3 and above.
541 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be
542 // made available is handled correctly.
543 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) {
544 if (GetParam() < kProtoSPDY3)
545 return;
547 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
548 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, true));
549 MockWrite writes[] = {
550 CreateMockWrite(*req.get(), 0),
551 CreateMockWrite(*chunk1, 1),
553 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
554 scoped_ptr<SpdyFrame> window_update(
555 spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize));
556 MockRead reads[] = {
557 CreateMockRead(*window_update, 2),
558 CreateMockRead(*resp, 3),
559 CreateMockRead(*chunk1, 4),
560 MockRead(ASYNC, 0, 5) // EOF
563 HostPortPair host_port_pair("www.google.com", 80);
564 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
565 kPrivacyModeDisabled);
567 InitSessionDeterministic(reads, arraysize(reads),
568 writes, arraysize(writes),
569 key);
571 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0);
573 HttpRequestInfo request;
574 request.method = "POST";
575 request.url = GURL("http://www.google.com/");
576 request.upload_data_stream = &upload_stream;
578 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback()));
579 upload_stream.AppendChunk(kUploadData, kUploadDataSize, true);
581 BoundNetLog net_log;
582 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
583 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
584 net_log, CompletionCallback()));
586 HttpRequestHeaders headers;
587 HttpResponseInfo response;
588 // This will attempt to Write() the initial request and headers, which will
589 // complete asynchronously.
590 TestCompletionCallback callback;
591 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
592 callback.callback()));
593 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
595 // Complete the initial request write and first chunk.
596 deterministic_data_->RunFor(2);
597 ASSERT_TRUE(callback.have_result());
598 EXPECT_EQ(OK, callback.WaitForResult());
600 // Verify that the window size has decreased.
601 ASSERT_TRUE(http_stream->stream() != NULL);
602 EXPECT_NE(static_cast<int>(kSpdyStreamInitialWindowSize),
603 http_stream->stream()->send_window_size());
605 // Read window update.
606 deterministic_data_->RunFor(1);
608 // Verify the window update.
609 ASSERT_TRUE(http_stream->stream() != NULL);
610 EXPECT_EQ(static_cast<int>(kSpdyStreamInitialWindowSize),
611 http_stream->stream()->send_window_size());
613 // Read response headers.
614 deterministic_data_->RunFor(1);
615 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
617 // Read and check |chunk1| response.
618 deterministic_data_->RunFor(1);
619 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
620 ASSERT_EQ(kUploadDataSize,
621 http_stream->ReadResponseBody(
622 buf1.get(), kUploadDataSize, callback.callback()));
623 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
625 // Finish reading the |EOF|.
626 deterministic_data_->RunFor(1);
627 ASSERT_TRUE(response.headers.get());
628 ASSERT_EQ(200, response.headers->response_code());
629 EXPECT_TRUE(deterministic_data_->at_read_eof());
630 EXPECT_TRUE(deterministic_data_->at_write_eof());
633 // TODO(willchan): Write a longer test for SpdyStream that exercises all
634 // methods.
636 } // namespace net