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"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/stl_util.h"
12 #include "crypto/ec_private_key.h"
13 #include "crypto/ec_signature_creator.h"
14 #include "crypto/signature_creator.h"
15 #include "net/base/capturing_net_log.h"
16 #include "net/base/load_timing_info.h"
17 #include "net/base/load_timing_info_test_util.h"
18 #include "net/base/upload_data_stream.h"
19 #include "net/base/upload_element_reader.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/socket/next_proto.h"
25 #include "net/socket/socket_test_util.h"
26 #include "net/spdy/spdy_http_utils.h"
27 #include "net/spdy/spdy_session.h"
28 #include "net/spdy/spdy_test_util_common.h"
29 #include "net/ssl/default_server_bound_cert_store.h"
30 #include "testing/gtest/include/gtest/gtest.h"
36 // Tests the load timing of a stream that's connected and is not the first
37 // request sent on a connection.
38 void TestLoadTimingReused(const HttpStream
& stream
) {
39 LoadTimingInfo load_timing_info
;
40 EXPECT_TRUE(stream
.GetLoadTimingInfo(&load_timing_info
));
42 EXPECT_TRUE(load_timing_info
.socket_reused
);
43 EXPECT_NE(NetLog::Source::kInvalidId
, load_timing_info
.socket_log_id
);
45 ExpectConnectTimingHasNoTimes(load_timing_info
.connect_timing
);
46 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info
);
49 // Tests the load timing of a stream that's connected and using a fresh
51 void TestLoadTimingNotReused(const HttpStream
& stream
) {
52 LoadTimingInfo load_timing_info
;
53 EXPECT_TRUE(stream
.GetLoadTimingInfo(&load_timing_info
));
55 EXPECT_FALSE(load_timing_info
.socket_reused
);
56 EXPECT_NE(NetLog::Source::kInvalidId
, load_timing_info
.socket_log_id
);
58 ExpectConnectTimingHasTimes(load_timing_info
.connect_timing
,
59 CONNECT_TIMING_HAS_DNS_TIMES
);
60 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info
);
65 class SpdyHttpStreamTest
: public testing::Test
,
66 public testing::WithParamInterface
<NextProto
> {
69 : spdy_util_(GetParam()),
70 session_deps_(GetParam()) {
71 session_deps_
.net_log
= &net_log_
;
74 DeterministicSocketData
* deterministic_data() {
75 return deterministic_data_
.get();
78 OrderedSocketData
* data() { return data_
.get(); }
81 virtual void TearDown() OVERRIDE
{
82 crypto::ECSignatureCreator::SetFactoryForTesting(NULL
);
83 base::MessageLoop::current()->RunUntilIdle();
86 // Initializes the session using DeterministicSocketData. It's advisable
87 // to use this function rather than the OrderedSocketData, since the
88 // DeterministicSocketData behaves in a reasonable manner.
89 void InitSessionDeterministic(MockRead
* reads
, size_t reads_count
,
90 MockWrite
* writes
, size_t writes_count
,
91 const SpdySessionKey
& key
) {
92 deterministic_data_
.reset(
93 new DeterministicSocketData(reads
, reads_count
, writes
, writes_count
));
94 session_deps_
.deterministic_socket_factory
->AddSocketDataProvider(
95 deterministic_data_
.get());
97 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
98 session_
= CreateInsecureSpdySession(http_session_
, key
, BoundNetLog());
101 // Initializes the session using the finicky OrderedSocketData class.
102 void InitSession(MockRead
* reads
, size_t reads_count
,
103 MockWrite
* writes
, size_t writes_count
,
104 const SpdySessionKey
& key
) {
105 data_
.reset(new OrderedSocketData(reads
, reads_count
,
106 writes
, writes_count
));
107 session_deps_
.socket_factory
->AddSocketDataProvider(data_
.get());
108 http_session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps_
);
109 session_
= CreateInsecureSpdySession(http_session_
, key
, BoundNetLog());
112 void TestSendCredentials(
113 ServerBoundCertService
* server_bound_cert_service
,
114 const std::string
& cert
,
115 const std::string
& proof
);
117 SpdyTestUtil spdy_util_
;
118 CapturingNetLog net_log_
;
119 SpdySessionDependencies session_deps_
;
120 scoped_ptr
<OrderedSocketData
> data_
;
121 scoped_ptr
<DeterministicSocketData
> deterministic_data_
;
122 scoped_refptr
<HttpNetworkSession
> http_session_
;
123 base::WeakPtr
<SpdySession
> session_
;
126 MockECSignatureCreatorFactory ec_signature_creator_factory_
;
129 INSTANTIATE_TEST_CASE_P(
132 testing::Values(kProtoDeprecatedSPDY2
,
133 kProtoSPDY3
, kProtoSPDY31
, kProtoSPDY4a2
,
134 kProtoHTTP2Draft04
));
136 // SpdyHttpStream::GetUploadProgress() should still work even before the
137 // stream is initialized.
138 TEST_P(SpdyHttpStreamTest
, GetUploadProgressBeforeInitialization
) {
140 MockRead(ASYNC
, 0, 0) // EOF
143 HostPortPair
host_port_pair("www.google.com", 80);
144 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
145 kPrivacyModeDisabled
);
146 InitSession(reads
, arraysize(reads
), NULL
, 0, key
);
148 SpdyHttpStream
stream(session_
, false);
149 UploadProgress progress
= stream
.GetUploadProgress();
150 EXPECT_EQ(0u, progress
.size());
151 EXPECT_EQ(0u, progress
.position());
154 TEST_P(SpdyHttpStreamTest
, SendRequest
) {
155 scoped_ptr
<SpdyFrame
> req(
156 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
157 MockWrite writes
[] = {
158 CreateMockWrite(*req
.get(), 1),
160 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
162 CreateMockRead(*resp
, 2),
163 MockRead(SYNCHRONOUS
, 0, 3) // EOF
166 HostPortPair
host_port_pair("www.google.com", 80);
167 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
168 kPrivacyModeDisabled
);
169 InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
), key
);
171 HttpRequestInfo request
;
172 request
.method
= "GET";
173 request
.url
= GURL("http://www.google.com/");
174 TestCompletionCallback callback
;
175 HttpResponseInfo response
;
176 HttpRequestHeaders headers
;
178 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
179 // Make sure getting load timing information the stream early does not crash.
180 LoadTimingInfo load_timing_info
;
181 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
185 http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
186 net_log
, CompletionCallback()));
187 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
189 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
190 callback
.callback()));
191 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
192 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
194 // This triggers the MockWrite and read 2
195 callback
.WaitForResult();
197 // Can get timing information once the stream connects.
198 TestLoadTimingNotReused(*http_stream
);
200 // This triggers read 3. The empty read causes the session to shut down.
201 data()->CompleteRead();
203 // Because we abandoned the stream, we don't expect to find a session in the
205 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
206 EXPECT_TRUE(data()->at_read_eof());
207 EXPECT_TRUE(data()->at_write_eof());
209 TestLoadTimingNotReused(*http_stream
);
210 http_stream
->Close(true);
211 // Test that there's no crash when trying to get the load timing after the
212 // stream has been closed.
213 TestLoadTimingNotReused(*http_stream
);
216 TEST_P(SpdyHttpStreamTest
, LoadTimingTwoRequests
) {
217 scoped_ptr
<SpdyFrame
> req1(
218 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
219 scoped_ptr
<SpdyFrame
> req2(
220 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 3, LOWEST
, true));
221 MockWrite writes
[] = {
222 CreateMockWrite(*req1
, 0),
223 CreateMockWrite(*req2
, 1),
225 scoped_ptr
<SpdyFrame
> resp1(
226 spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
227 scoped_ptr
<SpdyFrame
> body1(
228 spdy_util_
.ConstructSpdyBodyFrame(1, "", 0, true));
229 scoped_ptr
<SpdyFrame
> resp2(
230 spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 3));
231 scoped_ptr
<SpdyFrame
> body2(
232 spdy_util_
.ConstructSpdyBodyFrame(3, "", 0, true));
234 CreateMockRead(*resp1
, 2),
235 CreateMockRead(*body1
, 3),
236 CreateMockRead(*resp2
, 4),
237 CreateMockRead(*body2
, 5),
238 MockRead(ASYNC
, 0, 6) // EOF
241 HostPortPair
host_port_pair("www.google.com", 80);
242 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
243 kPrivacyModeDisabled
);
244 InitSessionDeterministic(reads
, arraysize(reads
),
245 writes
, arraysize(writes
),
248 HttpRequestInfo request1
;
249 request1
.method
= "GET";
250 request1
.url
= GURL("http://www.google.com/");
251 TestCompletionCallback callback1
;
252 HttpResponseInfo response1
;
253 HttpRequestHeaders headers1
;
254 scoped_ptr
<SpdyHttpStream
> http_stream1(new SpdyHttpStream(session_
, true));
256 HttpRequestInfo request2
;
257 request2
.method
= "GET";
258 request2
.url
= GURL("http://www.google.com/");
259 TestCompletionCallback callback2
;
260 HttpResponseInfo response2
;
261 HttpRequestHeaders headers2
;
262 scoped_ptr
<SpdyHttpStream
> http_stream2(new SpdyHttpStream(session_
, true));
266 http_stream1
->InitializeStream(&request1
, DEFAULT_PRIORITY
,
268 CompletionCallback()));
269 EXPECT_EQ(ERR_IO_PENDING
, http_stream1
->SendRequest(headers1
, &response1
,
270 callback1
.callback()));
271 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
273 deterministic_data()->RunFor(1);
274 EXPECT_LE(0, callback1
.WaitForResult());
276 TestLoadTimingNotReused(*http_stream1
);
277 LoadTimingInfo load_timing_info1
;
278 LoadTimingInfo load_timing_info2
;
279 EXPECT_TRUE(http_stream1
->GetLoadTimingInfo(&load_timing_info1
));
280 EXPECT_FALSE(http_stream2
->GetLoadTimingInfo(&load_timing_info2
));
284 http_stream2
->InitializeStream(&request2
, DEFAULT_PRIORITY
,
286 CompletionCallback()));
287 EXPECT_EQ(ERR_IO_PENDING
, http_stream2
->SendRequest(headers2
, &response2
,
288 callback2
.callback()));
289 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
291 deterministic_data()->RunFor(1);
292 EXPECT_LE(0, callback2
.WaitForResult());
293 TestLoadTimingReused(*http_stream2
);
294 EXPECT_TRUE(http_stream2
->GetLoadTimingInfo(&load_timing_info2
));
295 EXPECT_EQ(load_timing_info1
.socket_log_id
, load_timing_info2
.socket_log_id
);
298 deterministic_data()->RunFor(6);
300 // Read stream 1 to completion, before making sure we can still read load
301 // timing from both streams.
302 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(1));
304 0, http_stream1
->ReadResponseBody(buf1
.get(), 1, callback1
.callback()));
306 // Stream 1 has been read to completion.
307 TestLoadTimingNotReused(*http_stream1
);
308 // Stream 2 still has queued body data.
309 TestLoadTimingReused(*http_stream2
);
312 TEST_P(SpdyHttpStreamTest
, SendChunkedPost
) {
313 BufferedSpdyFramer
framer(spdy_util_
.spdy_version(), false);
315 scoped_ptr
<SpdyFrame
> req(
316 spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
317 scoped_ptr
<SpdyFrame
> body(
318 framer
.CreateDataFrame(1, kUploadData
, kUploadDataSize
, DATA_FLAG_FIN
));
319 std::vector
<MockWrite
> writes
;
321 writes
.push_back(CreateMockWrite(*req
, seq
++));
322 writes
.push_back(CreateMockWrite(*body
, seq
++)); // POST upload frame
324 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
325 std::vector
<MockRead
> reads
;
326 reads
.push_back(CreateMockRead(*resp
, seq
++));
327 reads
.push_back(CreateMockRead(*body
, seq
++));
328 reads
.push_back(MockRead(SYNCHRONOUS
, 0, seq
++)); // EOF
330 HostPortPair
host_port_pair("www.google.com", 80);
331 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
332 kPrivacyModeDisabled
);
333 InitSession(vector_as_array(&reads
), reads
.size(),
334 vector_as_array(&writes
), writes
.size(),
336 EXPECT_EQ(spdy_util_
.spdy_version(), session_
->GetProtocolVersion());
338 UploadDataStream
upload_stream(UploadDataStream::CHUNKED
, 0);
339 const int kFirstChunkSize
= kUploadDataSize
/2;
340 upload_stream
.AppendChunk(kUploadData
, kFirstChunkSize
, false);
341 upload_stream
.AppendChunk(kUploadData
+ kFirstChunkSize
,
342 kUploadDataSize
- kFirstChunkSize
, true);
344 HttpRequestInfo request
;
345 request
.method
= "POST";
346 request
.url
= GURL("http://www.google.com/");
347 request
.upload_data_stream
= &upload_stream
;
349 ASSERT_EQ(OK
, upload_stream
.Init(CompletionCallback()));
351 TestCompletionCallback callback
;
352 HttpResponseInfo response
;
353 HttpRequestHeaders headers
;
355 SpdyHttpStream
http_stream(session_
, true);
358 http_stream
.InitializeStream(&request
, DEFAULT_PRIORITY
,
359 net_log
, CompletionCallback()));
361 EXPECT_EQ(ERR_IO_PENDING
, http_stream
.SendRequest(
362 headers
, &response
, callback
.callback()));
363 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
365 // This results in writing the post body and reading the response headers.
366 callback
.WaitForResult();
368 // This triggers reading the body and the EOF, causing the session to shut
370 data()->CompleteRead();
371 base::MessageLoop::current()->RunUntilIdle();
373 // Because we abandoned the stream, we don't expect to find a session in the
375 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
376 EXPECT_TRUE(data()->at_read_eof());
377 EXPECT_TRUE(data()->at_write_eof());
380 // Test to ensure the SpdyStream state machine does not get confused when a
381 // chunk becomes available while a write is pending.
382 TEST_P(SpdyHttpStreamTest
, DelayedSendChunkedPost
) {
383 const char kUploadData1
[] = "12345678";
384 const int kUploadData1Size
= arraysize(kUploadData1
)-1;
385 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
386 scoped_ptr
<SpdyFrame
> chunk1(spdy_util_
.ConstructSpdyBodyFrame(1, false));
387 scoped_ptr
<SpdyFrame
> chunk2(
388 spdy_util_
.ConstructSpdyBodyFrame(
389 1, kUploadData1
, kUploadData1Size
, false));
390 scoped_ptr
<SpdyFrame
> chunk3(spdy_util_
.ConstructSpdyBodyFrame(1, true));
391 MockWrite writes
[] = {
392 CreateMockWrite(*req
.get(), 0),
393 CreateMockWrite(*chunk1
, 1), // POST upload frames
394 CreateMockWrite(*chunk2
, 2),
395 CreateMockWrite(*chunk3
, 3),
397 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
399 CreateMockRead(*resp
, 4),
400 CreateMockRead(*chunk1
, 5),
401 CreateMockRead(*chunk2
, 6),
402 CreateMockRead(*chunk3
, 7),
403 MockRead(ASYNC
, 0, 8) // EOF
406 HostPortPair
host_port_pair("www.google.com", 80);
407 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
408 kPrivacyModeDisabled
);
409 InitSessionDeterministic(reads
, arraysize(reads
),
410 writes
, arraysize(writes
),
413 UploadDataStream
upload_stream(UploadDataStream::CHUNKED
, 0);
415 HttpRequestInfo request
;
416 request
.method
= "POST";
417 request
.url
= GURL("http://www.google.com/");
418 request
.upload_data_stream
= &upload_stream
;
420 ASSERT_EQ(OK
, upload_stream
.Init(CompletionCallback()));
421 upload_stream
.AppendChunk(kUploadData
, kUploadDataSize
, false);
424 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
425 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
426 net_log
, CompletionCallback()));
428 TestCompletionCallback callback
;
429 HttpRequestHeaders headers
;
430 HttpResponseInfo response
;
431 // This will attempt to Write() the initial request and headers, which will
432 // complete asynchronously.
433 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
434 callback
.callback()));
435 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
437 // Complete the initial request write and the first chunk.
438 deterministic_data()->RunFor(2);
439 ASSERT_TRUE(callback
.have_result());
440 EXPECT_EQ(OK
, callback
.WaitForResult());
442 // Now append the final two chunks which will enqueue two more writes.
443 upload_stream
.AppendChunk(kUploadData1
, kUploadData1Size
, false);
444 upload_stream
.AppendChunk(kUploadData
, kUploadDataSize
, true);
446 // Finish writing all the chunks.
447 deterministic_data()->RunFor(2);
449 // Read response headers.
450 deterministic_data()->RunFor(1);
451 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
453 // Read and check |chunk1| response.
454 deterministic_data()->RunFor(1);
455 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(kUploadDataSize
));
456 ASSERT_EQ(kUploadDataSize
,
457 http_stream
->ReadResponseBody(
458 buf1
.get(), kUploadDataSize
, callback
.callback()));
459 EXPECT_EQ(kUploadData
, std::string(buf1
->data(), kUploadDataSize
));
461 // Read and check |chunk2| response.
462 deterministic_data()->RunFor(1);
463 scoped_refptr
<IOBuffer
> buf2(new IOBuffer(kUploadData1Size
));
464 ASSERT_EQ(kUploadData1Size
,
465 http_stream
->ReadResponseBody(
466 buf2
.get(), kUploadData1Size
, callback
.callback()));
467 EXPECT_EQ(kUploadData1
, std::string(buf2
->data(), kUploadData1Size
));
469 // Read and check |chunk3| response.
470 deterministic_data()->RunFor(1);
471 scoped_refptr
<IOBuffer
> buf3(new IOBuffer(kUploadDataSize
));
472 ASSERT_EQ(kUploadDataSize
,
473 http_stream
->ReadResponseBody(
474 buf3
.get(), kUploadDataSize
, callback
.callback()));
475 EXPECT_EQ(kUploadData
, std::string(buf3
->data(), kUploadDataSize
));
477 // Finish reading the |EOF|.
478 deterministic_data()->RunFor(1);
479 ASSERT_TRUE(response
.headers
.get());
480 ASSERT_EQ(200, response
.headers
->response_code());
481 EXPECT_TRUE(deterministic_data()->at_read_eof());
482 EXPECT_TRUE(deterministic_data()->at_write_eof());
485 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
486 TEST_P(SpdyHttpStreamTest
, SpdyURLTest
) {
487 const char * const full_url
= "http://www.google.com/foo?query=what#anchor";
488 const char * const base_url
= "http://www.google.com/foo?query=what";
489 scoped_ptr
<SpdyFrame
> req(
490 spdy_util_
.ConstructSpdyGet(base_url
, false, 1, LOWEST
));
491 MockWrite writes
[] = {
492 CreateMockWrite(*req
.get(), 1),
494 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
496 CreateMockRead(*resp
, 2),
497 MockRead(SYNCHRONOUS
, 0, 3) // EOF
500 HostPortPair
host_port_pair("www.google.com", 80);
501 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
502 kPrivacyModeDisabled
);
503 InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
), key
);
505 HttpRequestInfo request
;
506 request
.method
= "GET";
507 request
.url
= GURL(full_url
);
508 TestCompletionCallback callback
;
509 HttpResponseInfo response
;
510 HttpRequestHeaders headers
;
512 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
514 http_stream
->InitializeStream(
515 &request
, DEFAULT_PRIORITY
, net_log
, CompletionCallback()));
517 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
518 callback
.callback()));
520 EXPECT_EQ(base_url
, http_stream
->stream()->GetUrlFromHeaders().spec());
522 // This triggers the MockWrite and read 2
523 callback
.WaitForResult();
525 // This triggers read 3. The empty read causes the session to shut down.
526 data()->CompleteRead();
528 // Because we abandoned the stream, we don't expect to find a session in the
530 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
531 EXPECT_TRUE(data()->at_read_eof());
532 EXPECT_TRUE(data()->at_write_eof());
535 // The tests below are only for SPDY/3 and above.
537 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be
538 // made available is handled correctly.
539 TEST_P(SpdyHttpStreamTest
, DelayedSendChunkedPostWithWindowUpdate
) {
540 if (GetParam() < kProtoSPDY3
)
543 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
544 scoped_ptr
<SpdyFrame
> chunk1(spdy_util_
.ConstructSpdyBodyFrame(1, true));
545 MockWrite writes
[] = {
546 CreateMockWrite(*req
.get(), 0),
547 CreateMockWrite(*chunk1
, 1),
549 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
550 scoped_ptr
<SpdyFrame
> window_update(
551 spdy_util_
.ConstructSpdyWindowUpdate(1, kUploadDataSize
));
553 CreateMockRead(*window_update
, 2),
554 CreateMockRead(*resp
, 3),
555 CreateMockRead(*chunk1
, 4),
556 MockRead(ASYNC
, 0, 5) // EOF
559 HostPortPair
host_port_pair("www.google.com", 80);
560 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
561 kPrivacyModeDisabled
);
563 InitSessionDeterministic(reads
, arraysize(reads
),
564 writes
, arraysize(writes
),
567 UploadDataStream
upload_stream(UploadDataStream::CHUNKED
, 0);
569 HttpRequestInfo request
;
570 request
.method
= "POST";
571 request
.url
= GURL("http://www.google.com/");
572 request
.upload_data_stream
= &upload_stream
;
574 ASSERT_EQ(OK
, upload_stream
.Init(CompletionCallback()));
575 upload_stream
.AppendChunk(kUploadData
, kUploadDataSize
, true);
578 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
579 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
580 net_log
, CompletionCallback()));
582 HttpRequestHeaders headers
;
583 HttpResponseInfo response
;
584 // This will attempt to Write() the initial request and headers, which will
585 // complete asynchronously.
586 TestCompletionCallback callback
;
587 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
588 callback
.callback()));
589 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
591 // Complete the initial request write and first chunk.
592 deterministic_data_
->RunFor(2);
593 ASSERT_TRUE(callback
.have_result());
594 EXPECT_EQ(OK
, callback
.WaitForResult());
596 // Verify that the window size has decreased.
597 ASSERT_TRUE(http_stream
->stream() != NULL
);
598 EXPECT_NE(static_cast<int>(kSpdyStreamInitialWindowSize
),
599 http_stream
->stream()->send_window_size());
601 // Read window update.
602 deterministic_data_
->RunFor(1);
604 // Verify the window update.
605 ASSERT_TRUE(http_stream
->stream() != NULL
);
606 EXPECT_EQ(static_cast<int>(kSpdyStreamInitialWindowSize
),
607 http_stream
->stream()->send_window_size());
609 // Read response headers.
610 deterministic_data_
->RunFor(1);
611 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
613 // Read and check |chunk1| response.
614 deterministic_data_
->RunFor(1);
615 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(kUploadDataSize
));
616 ASSERT_EQ(kUploadDataSize
,
617 http_stream
->ReadResponseBody(
618 buf1
.get(), kUploadDataSize
, callback
.callback()));
619 EXPECT_EQ(kUploadData
, std::string(buf1
->data(), kUploadDataSize
));
621 // Finish reading the |EOF|.
622 deterministic_data_
->RunFor(1);
623 ASSERT_TRUE(response
.headers
.get());
624 ASSERT_EQ(200, response
.headers
->response_code());
625 EXPECT_TRUE(deterministic_data_
->at_read_eof());
626 EXPECT_TRUE(deterministic_data_
->at_write_eof());
629 // TODO(willchan): Write a longer test for SpdyStream that exercises all