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/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"
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
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
);
66 class SpdyHttpStreamTest
: public testing::Test
,
67 public testing::WithParamInterface
<NextProto
> {
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(); }
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());
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_
;
127 MockECSignatureCreatorFactory ec_signature_creator_factory_
;
130 INSTANTIATE_TEST_CASE_P(
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
) {
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));
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
;
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
));
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
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));
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
),
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));
270 http_stream1
->InitializeStream(&request1
, DEFAULT_PRIORITY
,
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
));
288 http_stream2
->InitializeStream(&request2
, DEFAULT_PRIORITY
,
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
);
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));
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
;
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(),
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
;
359 SpdyHttpStream
http_stream(session_
, true);
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
374 data()->CompleteRead();
375 base::MessageLoop::current()->RunUntilIdle();
377 // Because we abandoned the stream, we don't expect to find a session in the
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));
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
),
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);
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));
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
;
516 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
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
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
)
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
));
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
),
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);
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