Raise SIGKILL if cast_service doesn't Finalize() within timeout.
[chromium-blink-merge.git] / net / spdy / spdy_http_stream_unittest.cc
blob11f4e69dac621d319d403588a7f0b33b3cfecc1a
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/chunked_upload_data_stream.h"
18 #include "net/base/load_timing_info.h"
19 #include "net/base/load_timing_info_test_util.h"
20 #include "net/base/test_completion_callback.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_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 OrderedSocketData* data() { return data_.get(); }
81 protected:
82 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 ChannelIDService* channel_id_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(NextProto,
131 SpdyHttpStreamTest,
132 testing::Values(kProtoSPDY31,
133 kProtoSPDY4_14,
134 kProtoSPDY4));
136 // SpdyHttpStream::GetUploadProgress() should still work even before the
137 // stream is initialized.
138 TEST_P(SpdyHttpStreamTest, GetUploadProgressBeforeInitialization) {
139 MockRead reads[] = {
140 MockRead(ASYNC, 0, 0) // EOF
143 HostPortPair host_port_pair("www.google.com", 80);
144 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
145 PRIVACY_MODE_DISABLED);
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());
153 // Pump the event loop so |reads| is consumed before the function returns.
154 base::RunLoop().RunUntilIdle();
157 TEST_P(SpdyHttpStreamTest, SendRequest) {
158 scoped_ptr<SpdyFrame> req(
159 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
160 MockWrite writes[] = {
161 CreateMockWrite(*req.get(), 1),
163 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
164 MockRead reads[] = {
165 CreateMockRead(*resp, 2),
166 MockRead(SYNCHRONOUS, 0, 3) // EOF
169 HostPortPair host_port_pair("www.google.com", 80);
170 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
171 PRIVACY_MODE_DISABLED);
172 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
174 HttpRequestInfo request;
175 request.method = "GET";
176 request.url = GURL("http://www.google.com/");
177 TestCompletionCallback callback;
178 HttpResponseInfo response;
179 HttpRequestHeaders headers;
180 BoundNetLog net_log;
181 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
182 // Make sure getting load timing information the stream early does not crash.
183 LoadTimingInfo load_timing_info;
184 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
186 ASSERT_EQ(
188 http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
189 net_log, CompletionCallback()));
190 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
192 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
193 callback.callback()));
194 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
195 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
197 // This triggers the MockWrite and read 2
198 callback.WaitForResult();
200 // Can get timing information once the stream connects.
201 TestLoadTimingNotReused(*http_stream);
203 // This triggers read 3. The empty read causes the session to shut down.
204 data()->CompleteRead();
206 // Because we abandoned the stream, we don't expect to find a session in the
207 // pool anymore.
208 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
209 EXPECT_TRUE(data()->at_read_eof());
210 EXPECT_TRUE(data()->at_write_eof());
212 TestLoadTimingNotReused(*http_stream);
213 http_stream->Close(true);
214 // Test that there's no crash when trying to get the load timing after the
215 // stream has been closed.
216 TestLoadTimingNotReused(*http_stream);
219 TEST_P(SpdyHttpStreamTest, LoadTimingTwoRequests) {
220 scoped_ptr<SpdyFrame> req1(
221 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
222 scoped_ptr<SpdyFrame> req2(
223 spdy_util_.ConstructSpdyGet(NULL, 0, false, 3, LOWEST, true));
224 MockWrite writes[] = {
225 CreateMockWrite(*req1, 0),
226 CreateMockWrite(*req2, 1),
228 scoped_ptr<SpdyFrame> resp1(
229 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
230 scoped_ptr<SpdyFrame> body1(
231 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
232 scoped_ptr<SpdyFrame> resp2(
233 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
234 scoped_ptr<SpdyFrame> body2(
235 spdy_util_.ConstructSpdyBodyFrame(3, "", 0, true));
236 MockRead reads[] = {
237 CreateMockRead(*resp1, 2),
238 CreateMockRead(*body1, 3),
239 CreateMockRead(*resp2, 4),
240 CreateMockRead(*body2, 5),
241 MockRead(ASYNC, 0, 6) // EOF
244 HostPortPair host_port_pair("www.google.com", 80);
245 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
246 PRIVACY_MODE_DISABLED);
247 InitSessionDeterministic(reads, arraysize(reads),
248 writes, arraysize(writes),
249 key);
251 HttpRequestInfo request1;
252 request1.method = "GET";
253 request1.url = GURL("http://www.google.com/");
254 TestCompletionCallback callback1;
255 HttpResponseInfo response1;
256 HttpRequestHeaders headers1;
257 scoped_ptr<SpdyHttpStream> http_stream1(new SpdyHttpStream(session_, true));
259 HttpRequestInfo request2;
260 request2.method = "GET";
261 request2.url = GURL("http://www.google.com/");
262 TestCompletionCallback callback2;
263 HttpResponseInfo response2;
264 HttpRequestHeaders headers2;
265 scoped_ptr<SpdyHttpStream> http_stream2(new SpdyHttpStream(session_, true));
267 // First write.
268 ASSERT_EQ(OK,
269 http_stream1->InitializeStream(&request1, DEFAULT_PRIORITY,
270 BoundNetLog(),
271 CompletionCallback()));
272 EXPECT_EQ(ERR_IO_PENDING, http_stream1->SendRequest(headers1, &response1,
273 callback1.callback()));
274 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
276 deterministic_data()->RunFor(1);
277 EXPECT_LE(0, callback1.WaitForResult());
279 TestLoadTimingNotReused(*http_stream1);
280 LoadTimingInfo load_timing_info1;
281 LoadTimingInfo load_timing_info2;
282 EXPECT_TRUE(http_stream1->GetLoadTimingInfo(&load_timing_info1));
283 EXPECT_FALSE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
285 // Second write.
286 ASSERT_EQ(OK,
287 http_stream2->InitializeStream(&request2, DEFAULT_PRIORITY,
288 BoundNetLog(),
289 CompletionCallback()));
290 EXPECT_EQ(ERR_IO_PENDING, http_stream2->SendRequest(headers2, &response2,
291 callback2.callback()));
292 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
294 deterministic_data()->RunFor(1);
295 EXPECT_LE(0, callback2.WaitForResult());
296 TestLoadTimingReused(*http_stream2);
297 EXPECT_TRUE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
298 EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id);
300 // All the reads.
301 deterministic_data()->RunFor(6);
303 // Read stream 1 to completion, before making sure we can still read load
304 // timing from both streams.
305 scoped_refptr<IOBuffer> buf1(new IOBuffer(1));
306 ASSERT_EQ(
307 0, http_stream1->ReadResponseBody(buf1.get(), 1, callback1.callback()));
309 // Stream 1 has been read to completion.
310 TestLoadTimingNotReused(*http_stream1);
311 // Stream 2 still has queued body data.
312 TestLoadTimingReused(*http_stream2);
315 TEST_P(SpdyHttpStreamTest, SendChunkedPost) {
316 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
318 scoped_ptr<SpdyFrame> req(
319 spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
320 scoped_ptr<SpdyFrame> body(
321 framer.CreateDataFrame(1, kUploadData, kUploadDataSize, DATA_FLAG_FIN));
322 std::vector<MockWrite> writes;
323 int seq = 0;
324 writes.push_back(CreateMockWrite(*req, seq++));
325 writes.push_back(CreateMockWrite(*body, seq++)); // POST upload frame
327 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
328 std::vector<MockRead> reads;
329 reads.push_back(CreateMockRead(*resp, seq++));
330 reads.push_back(CreateMockRead(*body, seq++));
331 reads.push_back(MockRead(SYNCHRONOUS, 0, seq++)); // EOF
333 HostPortPair host_port_pair("www.google.com", 80);
334 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
335 PRIVACY_MODE_DISABLED);
336 InitSession(vector_as_array(&reads), reads.size(),
337 vector_as_array(&writes), writes.size(),
338 key);
339 EXPECT_EQ(spdy_util_.spdy_version(), session_->GetProtocolVersion());
341 ChunkedUploadDataStream upload_stream(0);
342 const int kFirstChunkSize = kUploadDataSize/2;
343 upload_stream.AppendData(kUploadData, kFirstChunkSize, false);
344 upload_stream.AppendData(kUploadData + kFirstChunkSize,
345 kUploadDataSize - kFirstChunkSize, true);
347 HttpRequestInfo request;
348 request.method = "POST";
349 request.url = GURL("http://www.google.com/");
350 request.upload_data_stream = &upload_stream;
352 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
354 TestCompletionCallback callback;
355 HttpResponseInfo response;
356 HttpRequestHeaders headers;
357 BoundNetLog net_log;
358 SpdyHttpStream http_stream(session_, true);
359 ASSERT_EQ(
361 http_stream.InitializeStream(&request, DEFAULT_PRIORITY,
362 net_log, CompletionCallback()));
364 EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest(
365 headers, &response, callback.callback()));
366 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
368 // This results in writing the post body and reading the response headers.
369 callback.WaitForResult();
371 // This triggers reading the body and the EOF, causing the session to shut
372 // down.
373 data()->CompleteRead();
374 base::MessageLoop::current()->RunUntilIdle();
376 // Because we abandoned the stream, we don't expect to find a session in the
377 // pool anymore.
378 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
379 EXPECT_TRUE(data()->at_read_eof());
380 EXPECT_TRUE(data()->at_write_eof());
383 // Test to ensure the SpdyStream state machine does not get confused when a
384 // chunk becomes available while a write is pending.
385 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPost) {
386 const char kUploadData1[] = "12345678";
387 const int kUploadData1Size = arraysize(kUploadData1)-1;
388 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
389 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false));
390 scoped_ptr<SpdyFrame> chunk2(
391 spdy_util_.ConstructSpdyBodyFrame(
392 1, kUploadData1, kUploadData1Size, false));
393 scoped_ptr<SpdyFrame> chunk3(spdy_util_.ConstructSpdyBodyFrame(1, true));
394 MockWrite writes[] = {
395 CreateMockWrite(*req.get(), 0),
396 CreateMockWrite(*chunk1, 1), // POST upload frames
397 CreateMockWrite(*chunk2, 2),
398 CreateMockWrite(*chunk3, 3),
400 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
401 MockRead reads[] = {
402 CreateMockRead(*resp, 4),
403 CreateMockRead(*chunk1, 5),
404 CreateMockRead(*chunk2, 6),
405 CreateMockRead(*chunk3, 7),
406 MockRead(ASYNC, 0, 8) // EOF
409 HostPortPair host_port_pair("www.google.com", 80);
410 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
411 PRIVACY_MODE_DISABLED);
412 InitSessionDeterministic(reads, arraysize(reads),
413 writes, arraysize(writes),
414 key);
416 ChunkedUploadDataStream upload_stream(0);
418 HttpRequestInfo request;
419 request.method = "POST";
420 request.url = GURL("http://www.google.com/");
421 request.upload_data_stream = &upload_stream;
423 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
424 upload_stream.AppendData(kUploadData, kUploadDataSize, false);
426 BoundNetLog net_log;
427 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
428 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
429 net_log, CompletionCallback()));
431 TestCompletionCallback callback;
432 HttpRequestHeaders headers;
433 HttpResponseInfo response;
434 // This will attempt to Write() the initial request and headers, which will
435 // complete asynchronously.
436 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
437 callback.callback()));
438 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
440 // Complete the initial request write and the first chunk.
441 deterministic_data()->RunFor(2);
442 ASSERT_TRUE(callback.have_result());
443 EXPECT_EQ(OK, callback.WaitForResult());
445 // Now append the final two chunks which will enqueue two more writes.
446 upload_stream.AppendData(kUploadData1, kUploadData1Size, false);
447 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
449 // Finish writing all the chunks.
450 deterministic_data()->RunFor(2);
452 // Read response headers.
453 deterministic_data()->RunFor(1);
454 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
456 // Read and check |chunk1| response.
457 deterministic_data()->RunFor(1);
458 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
459 ASSERT_EQ(kUploadDataSize,
460 http_stream->ReadResponseBody(
461 buf1.get(), kUploadDataSize, callback.callback()));
462 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
464 // Read and check |chunk2| response.
465 deterministic_data()->RunFor(1);
466 scoped_refptr<IOBuffer> buf2(new IOBuffer(kUploadData1Size));
467 ASSERT_EQ(kUploadData1Size,
468 http_stream->ReadResponseBody(
469 buf2.get(), kUploadData1Size, callback.callback()));
470 EXPECT_EQ(kUploadData1, std::string(buf2->data(), kUploadData1Size));
472 // Read and check |chunk3| response.
473 deterministic_data()->RunFor(1);
474 scoped_refptr<IOBuffer> buf3(new IOBuffer(kUploadDataSize));
475 ASSERT_EQ(kUploadDataSize,
476 http_stream->ReadResponseBody(
477 buf3.get(), kUploadDataSize, callback.callback()));
478 EXPECT_EQ(kUploadData, std::string(buf3->data(), kUploadDataSize));
480 // Finish reading the |EOF|.
481 deterministic_data()->RunFor(1);
482 ASSERT_TRUE(response.headers.get());
483 ASSERT_EQ(200, response.headers->response_code());
484 EXPECT_TRUE(deterministic_data()->at_read_eof());
485 EXPECT_TRUE(deterministic_data()->at_write_eof());
488 // Test that the SpdyStream state machine can handle sending a final empty data
489 // frame when uploading a chunked data stream.
490 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithEmptyFinalDataFrame) {
491 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
492 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false));
493 scoped_ptr<SpdyFrame> chunk2(
494 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
495 MockWrite writes[] = {
496 CreateMockWrite(*req.get(), 0),
497 CreateMockWrite(*chunk1, 1), // POST upload frames
498 CreateMockWrite(*chunk2, 2),
500 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
501 MockRead reads[] = {
502 CreateMockRead(*resp, 3),
503 CreateMockRead(*chunk1, 4),
504 CreateMockRead(*chunk2, 5),
505 MockRead(ASYNC, 0, 6) // EOF
508 HostPortPair host_port_pair("www.google.com", 80);
509 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
510 PRIVACY_MODE_DISABLED);
511 InitSessionDeterministic(reads, arraysize(reads),
512 writes, arraysize(writes),
513 key);
515 ChunkedUploadDataStream upload_stream(0);
517 HttpRequestInfo request;
518 request.method = "POST";
519 request.url = GURL("http://www.google.com/");
520 request.upload_data_stream = &upload_stream;
522 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
523 upload_stream.AppendData(kUploadData, kUploadDataSize, false);
525 BoundNetLog net_log;
526 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
527 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
528 net_log, CompletionCallback()));
530 TestCompletionCallback callback;
531 HttpRequestHeaders headers;
532 HttpResponseInfo response;
533 // This will attempt to Write() the initial request and headers, which will
534 // complete asynchronously.
535 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
536 callback.callback()));
537 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
539 // Complete the initial request write and the first chunk.
540 deterministic_data()->RunFor(2);
541 ASSERT_TRUE(callback.have_result());
542 EXPECT_EQ(OK, callback.WaitForResult());
544 // Now end the stream with an empty data frame and the FIN set.
545 upload_stream.AppendData(NULL, 0, true);
547 // Finish writing the final frame.
548 deterministic_data()->RunFor(1);
550 // Read response headers.
551 deterministic_data()->RunFor(1);
552 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
554 // Read and check |chunk1| response.
555 deterministic_data()->RunFor(1);
556 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
557 ASSERT_EQ(kUploadDataSize,
558 http_stream->ReadResponseBody(
559 buf1.get(), kUploadDataSize, callback.callback()));
560 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
562 // Read and check |chunk2| response.
563 deterministic_data()->RunFor(1);
564 ASSERT_EQ(0,
565 http_stream->ReadResponseBody(
566 buf1.get(), kUploadDataSize, callback.callback()));
568 // Finish reading the |EOF|.
569 deterministic_data()->RunFor(1);
570 ASSERT_TRUE(response.headers.get());
571 ASSERT_EQ(200, response.headers->response_code());
572 EXPECT_TRUE(deterministic_data()->at_read_eof());
573 EXPECT_TRUE(deterministic_data()->at_write_eof());
576 // Test that the SpdyStream state machine handles a chunked upload with no
577 // payload. Unclear if this is a case worth supporting.
578 TEST_P(SpdyHttpStreamTest, ChunkedPostWithEmptyPayload) {
579 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
580 scoped_ptr<SpdyFrame> chunk(
581 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
582 MockWrite writes[] = {
583 CreateMockWrite(*req.get(), 0),
584 CreateMockWrite(*chunk, 1),
586 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
587 MockRead reads[] = {
588 CreateMockRead(*resp, 2),
589 CreateMockRead(*chunk, 3),
590 MockRead(ASYNC, 0, 4) // EOF
593 HostPortPair host_port_pair("www.google.com", 80);
594 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
595 PRIVACY_MODE_DISABLED);
596 InitSessionDeterministic(reads, arraysize(reads),
597 writes, arraysize(writes),
598 key);
600 ChunkedUploadDataStream upload_stream(0);
602 HttpRequestInfo request;
603 request.method = "POST";
604 request.url = GURL("http://www.google.com/");
605 request.upload_data_stream = &upload_stream;
607 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
608 upload_stream.AppendData("", 0, true);
610 BoundNetLog net_log;
611 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
612 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
613 net_log, CompletionCallback()));
615 TestCompletionCallback callback;
616 HttpRequestHeaders headers;
617 HttpResponseInfo response;
618 // This will attempt to Write() the initial request and headers, which will
619 // complete asynchronously.
620 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
621 callback.callback()));
622 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
624 // Complete writing request, followed by a FIN.
625 deterministic_data()->RunFor(2);
626 ASSERT_TRUE(callback.have_result());
627 EXPECT_EQ(OK, callback.WaitForResult());
629 // Read response headers.
630 deterministic_data()->RunFor(1);
631 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
633 // Read and check |chunk| response.
634 deterministic_data()->RunFor(1);
635 scoped_refptr<IOBuffer> buf(new IOBuffer(1));
636 ASSERT_EQ(0,
637 http_stream->ReadResponseBody(
638 buf.get(), 1, callback.callback()));
640 // Finish reading the |EOF|.
641 deterministic_data()->RunFor(1);
642 ASSERT_TRUE(response.headers.get());
643 ASSERT_EQ(200, response.headers->response_code());
644 EXPECT_TRUE(deterministic_data()->at_read_eof());
645 EXPECT_TRUE(deterministic_data()->at_write_eof());
648 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
649 TEST_P(SpdyHttpStreamTest, SpdyURLTest) {
650 const char * const full_url = "http://www.google.com/foo?query=what#anchor";
651 const char * const base_url = "http://www.google.com/foo?query=what";
652 scoped_ptr<SpdyFrame> req(
653 spdy_util_.ConstructSpdyGet(base_url, false, 1, LOWEST));
654 MockWrite writes[] = {
655 CreateMockWrite(*req.get(), 1),
657 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
658 MockRead reads[] = {
659 CreateMockRead(*resp, 2),
660 MockRead(SYNCHRONOUS, 0, 3) // EOF
663 HostPortPair host_port_pair("www.google.com", 80);
664 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
665 PRIVACY_MODE_DISABLED);
666 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
668 HttpRequestInfo request;
669 request.method = "GET";
670 request.url = GURL(full_url);
671 TestCompletionCallback callback;
672 HttpResponseInfo response;
673 HttpRequestHeaders headers;
674 BoundNetLog net_log;
675 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
676 ASSERT_EQ(OK,
677 http_stream->InitializeStream(
678 &request, DEFAULT_PRIORITY, net_log, CompletionCallback()));
680 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
681 callback.callback()));
683 EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec());
685 // This triggers the MockWrite and read 2
686 callback.WaitForResult();
688 // This triggers read 3. The empty read causes the session to shut down.
689 data()->CompleteRead();
691 // Because we abandoned the stream, we don't expect to find a session in the
692 // pool anymore.
693 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
694 EXPECT_TRUE(data()->at_read_eof());
695 EXPECT_TRUE(data()->at_write_eof());
698 // The tests below are only for SPDY/3 and above.
700 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be
701 // made available is handled correctly.
702 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) {
703 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
704 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, true));
705 MockWrite writes[] = {
706 CreateMockWrite(*req.get(), 0),
707 CreateMockWrite(*chunk1, 1),
709 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
710 scoped_ptr<SpdyFrame> window_update(
711 spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize));
712 MockRead reads[] = {
713 CreateMockRead(*window_update, 2),
714 CreateMockRead(*resp, 3),
715 CreateMockRead(*chunk1, 4),
716 MockRead(ASYNC, 0, 5) // EOF
719 HostPortPair host_port_pair("www.google.com", 80);
720 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
721 PRIVACY_MODE_DISABLED);
723 InitSessionDeterministic(reads, arraysize(reads),
724 writes, arraysize(writes),
725 key);
727 ChunkedUploadDataStream upload_stream(0);
729 HttpRequestInfo request;
730 request.method = "POST";
731 request.url = GURL("http://www.google.com/");
732 request.upload_data_stream = &upload_stream;
734 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
735 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
737 BoundNetLog net_log;
738 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
739 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
740 net_log, CompletionCallback()));
742 HttpRequestHeaders headers;
743 HttpResponseInfo response;
744 // This will attempt to Write() the initial request and headers, which will
745 // complete asynchronously.
746 TestCompletionCallback callback;
747 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
748 callback.callback()));
749 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
751 // Complete the initial request write and first chunk.
752 deterministic_data_->RunFor(2);
753 ASSERT_TRUE(callback.have_result());
754 EXPECT_EQ(OK, callback.WaitForResult());
756 // Verify that the window size has decreased.
757 ASSERT_TRUE(http_stream->stream() != NULL);
758 EXPECT_NE(
759 static_cast<int>(SpdySession::GetInitialWindowSize(session_->protocol())),
760 http_stream->stream()->send_window_size());
762 // Read window update.
763 deterministic_data_->RunFor(1);
765 // Verify the window update.
766 ASSERT_TRUE(http_stream->stream() != NULL);
767 EXPECT_EQ(
768 static_cast<int>(SpdySession::GetInitialWindowSize(session_->protocol())),
769 http_stream->stream()->send_window_size());
771 // Read response headers.
772 deterministic_data_->RunFor(1);
773 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
775 // Read and check |chunk1| response.
776 deterministic_data_->RunFor(1);
777 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
778 ASSERT_EQ(kUploadDataSize,
779 http_stream->ReadResponseBody(
780 buf1.get(), kUploadDataSize, callback.callback()));
781 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
783 // Finish reading the |EOF|.
784 deterministic_data_->RunFor(1);
785 ASSERT_TRUE(response.headers.get());
786 ASSERT_EQ(200, response.headers->response_code());
787 EXPECT_TRUE(deterministic_data_->at_read_eof());
788 EXPECT_TRUE(deterministic_data_->at_write_eof());
791 // TODO(willchan): Write a longer test for SpdyStream that exercises all
792 // methods.
794 } // namespace net