Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / net / spdy / spdy_http_stream_unittest.cc
blob09e4fcc542d855d7778d038c3bf718b45f04512b
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 "base/memory/scoped_ptr.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/run_loop.h"
10 #include "base/stl_util.h"
11 #include "crypto/ec_private_key.h"
12 #include "crypto/ec_signature_creator.h"
13 #include "crypto/signature_creator.h"
14 #include "net/base/chunked_upload_data_stream.h"
15 #include "net/base/load_timing_info.h"
16 #include "net/base/load_timing_info_test_util.h"
17 #include "net/base/test_completion_callback.h"
18 #include "net/cert/asn1_util.h"
19 #include "net/http/http_request_info.h"
20 #include "net/http/http_response_headers.h"
21 #include "net/http/http_response_info.h"
22 #include "net/log/test_net_log.h"
23 #include "net/socket/next_proto.h"
24 #include "net/socket/socket_test_util.h"
25 #include "net/spdy/spdy_http_utils.h"
26 #include "net/spdy/spdy_session.h"
27 #include "net/spdy/spdy_test_util_common.h"
28 #include "net/ssl/default_channel_id_store.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 namespace net {
33 namespace {
35 // Tests the load timing of a stream that's connected and is not the first
36 // request sent on a connection.
37 void TestLoadTimingReused(const HttpStream& stream) {
38 LoadTimingInfo load_timing_info;
39 EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info));
41 EXPECT_TRUE(load_timing_info.socket_reused);
42 EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id);
44 ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
45 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
48 // Tests the load timing of a stream that's connected and using a fresh
49 // connection.
50 void TestLoadTimingNotReused(const HttpStream& stream) {
51 LoadTimingInfo load_timing_info;
52 EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info));
54 EXPECT_FALSE(load_timing_info.socket_reused);
55 EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id);
57 ExpectConnectTimingHasTimes(load_timing_info.connect_timing,
58 CONNECT_TIMING_HAS_DNS_TIMES);
59 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
62 } // namespace
64 class SpdyHttpStreamTest : public testing::Test,
65 public testing::WithParamInterface<NextProto> {
66 public:
67 SpdyHttpStreamTest()
68 : spdy_util_(GetParam()),
69 session_deps_(GetParam()) {
70 session_deps_.net_log = &net_log_;
73 protected:
74 void TearDown() override {
75 crypto::ECSignatureCreator::SetFactoryForTesting(NULL);
76 base::MessageLoop::current()->RunUntilIdle();
77 EXPECT_TRUE(sequenced_data_->AllReadDataConsumed());
78 EXPECT_TRUE(sequenced_data_->AllWriteDataConsumed());
81 // Initializes the session using SequencedSocketData.
82 void InitSession(MockRead* reads,
83 size_t reads_count,
84 MockWrite* writes,
85 size_t writes_count,
86 const SpdySessionKey& key) {
87 sequenced_data_.reset(
88 new SequencedSocketData(reads, reads_count, writes, writes_count));
89 session_deps_.socket_factory->AddSocketDataProvider(sequenced_data_.get());
90 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
91 session_ = CreateInsecureSpdySession(http_session_, key, BoundNetLog());
94 void TestSendCredentials(
95 ChannelIDService* channel_id_service,
96 const std::string& cert,
97 const std::string& proof);
99 SpdyTestUtil spdy_util_;
100 TestNetLog net_log_;
101 SpdySessionDependencies session_deps_;
102 scoped_ptr<SequencedSocketData> sequenced_data_;
103 scoped_refptr<HttpNetworkSession> http_session_;
104 base::WeakPtr<SpdySession> session_;
106 private:
107 MockECSignatureCreatorFactory ec_signature_creator_factory_;
110 INSTANTIATE_TEST_CASE_P(NextProto,
111 SpdyHttpStreamTest,
112 testing::Values(kProtoSPDY31,
113 kProtoHTTP2));
115 // SpdyHttpStream::GetUploadProgress() should still work even before the
116 // stream is initialized.
117 TEST_P(SpdyHttpStreamTest, GetUploadProgressBeforeInitialization) {
118 MockRead reads[] = {
119 MockRead(ASYNC, 0, 0) // EOF
122 HostPortPair host_port_pair("www.example.org", 80);
123 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
124 PRIVACY_MODE_DISABLED);
125 InitSession(reads, arraysize(reads), NULL, 0, key);
127 SpdyHttpStream stream(session_, false);
128 UploadProgress progress = stream.GetUploadProgress();
129 EXPECT_EQ(0u, progress.size());
130 EXPECT_EQ(0u, progress.position());
132 // Pump the event loop so |reads| is consumed before the function returns.
133 base::RunLoop().RunUntilIdle();
136 TEST_P(SpdyHttpStreamTest, SendRequest) {
137 scoped_ptr<SpdyFrame> req(
138 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
139 MockWrite writes[] = {
140 CreateMockWrite(*req.get(), 0),
142 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
143 MockRead reads[] = {
144 CreateMockRead(*resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF
147 HostPortPair host_port_pair("www.example.org", 80);
148 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
149 PRIVACY_MODE_DISABLED);
150 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
152 HttpRequestInfo request;
153 request.method = "GET";
154 request.url = GURL("http://www.example.org/");
155 TestCompletionCallback callback;
156 HttpResponseInfo response;
157 HttpRequestHeaders headers;
158 BoundNetLog net_log;
159 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
160 // Make sure getting load timing information the stream early does not crash.
161 LoadTimingInfo load_timing_info;
162 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
164 ASSERT_EQ(
166 http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
167 net_log, CompletionCallback()));
168 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
170 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
171 callback.callback()));
172 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
173 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
175 callback.WaitForResult();
177 // Can get timing information once the stream connects.
178 TestLoadTimingNotReused(*http_stream);
180 // Because we abandoned the stream, we don't expect to find a session in the
181 // pool anymore.
182 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
184 TestLoadTimingNotReused(*http_stream);
185 http_stream->Close(true);
186 // Test that there's no crash when trying to get the load timing after the
187 // stream has been closed.
188 TestLoadTimingNotReused(*http_stream);
191 TEST_P(SpdyHttpStreamTest, LoadTimingTwoRequests) {
192 scoped_ptr<SpdyFrame> req1(
193 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
194 scoped_ptr<SpdyFrame> req2(
195 spdy_util_.ConstructSpdyGet(NULL, 0, false, 3, LOWEST, true));
196 MockWrite writes[] = {
197 CreateMockWrite(*req1, 0),
198 CreateMockWrite(*req2, 1),
200 scoped_ptr<SpdyFrame> resp1(
201 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
202 scoped_ptr<SpdyFrame> body1(
203 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
204 scoped_ptr<SpdyFrame> resp2(
205 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
206 scoped_ptr<SpdyFrame> body2(
207 spdy_util_.ConstructSpdyBodyFrame(3, "", 0, true));
208 MockRead reads[] = {
209 CreateMockRead(*resp1, 2),
210 CreateMockRead(*body1, 3),
211 CreateMockRead(*resp2, 4),
212 CreateMockRead(*body2, 5),
213 MockRead(ASYNC, 0, 6) // EOF
216 HostPortPair host_port_pair("www.example.org", 80);
217 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
218 PRIVACY_MODE_DISABLED);
219 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
221 HttpRequestInfo request1;
222 request1.method = "GET";
223 request1.url = GURL("http://www.example.org/");
224 TestCompletionCallback callback1;
225 HttpResponseInfo response1;
226 HttpRequestHeaders headers1;
227 scoped_ptr<SpdyHttpStream> http_stream1(new SpdyHttpStream(session_, true));
229 HttpRequestInfo request2;
230 request2.method = "GET";
231 request2.url = GURL("http://www.example.org/");
232 TestCompletionCallback callback2;
233 HttpResponseInfo response2;
234 HttpRequestHeaders headers2;
235 scoped_ptr<SpdyHttpStream> http_stream2(new SpdyHttpStream(session_, true));
237 // First write.
238 ASSERT_EQ(OK,
239 http_stream1->InitializeStream(&request1, DEFAULT_PRIORITY,
240 BoundNetLog(),
241 CompletionCallback()));
242 EXPECT_EQ(ERR_IO_PENDING, http_stream1->SendRequest(headers1, &response1,
243 callback1.callback()));
244 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
246 EXPECT_LE(0, callback1.WaitForResult());
248 TestLoadTimingNotReused(*http_stream1);
249 LoadTimingInfo load_timing_info1;
250 LoadTimingInfo load_timing_info2;
251 EXPECT_TRUE(http_stream1->GetLoadTimingInfo(&load_timing_info1));
252 EXPECT_FALSE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
254 // Second write.
255 ASSERT_EQ(OK,
256 http_stream2->InitializeStream(&request2, DEFAULT_PRIORITY,
257 BoundNetLog(),
258 CompletionCallback()));
259 EXPECT_EQ(ERR_IO_PENDING, http_stream2->SendRequest(headers2, &response2,
260 callback2.callback()));
261 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
263 EXPECT_LE(0, callback2.WaitForResult());
264 TestLoadTimingReused(*http_stream2);
265 EXPECT_TRUE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
266 EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id);
268 // Read stream 1 to completion, before making sure we can still read load
269 // timing from both streams.
270 scoped_refptr<IOBuffer> buf1(new IOBuffer(1));
271 ASSERT_EQ(
272 0, http_stream1->ReadResponseBody(buf1.get(), 1, callback1.callback()));
274 // Stream 1 has been read to completion.
275 TestLoadTimingNotReused(*http_stream1);
276 // Stream 2 still has queued body data.
277 TestLoadTimingReused(*http_stream2);
280 TEST_P(SpdyHttpStreamTest, SendChunkedPost) {
281 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
283 scoped_ptr<SpdyFrame> req(
284 spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
285 scoped_ptr<SpdyFrame> body(
286 framer.CreateDataFrame(1, kUploadData, kUploadDataSize, DATA_FLAG_FIN));
287 MockWrite writes[] = {
288 CreateMockWrite(*req, 0), // request
289 CreateMockWrite(*body, 1) // POST upload frame
292 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
293 MockRead reads[] = {
294 CreateMockRead(*resp, 2),
295 CreateMockRead(*body, 3),
296 MockRead(SYNCHRONOUS, 0, 4) // EOF
299 HostPortPair host_port_pair("www.example.org", 80);
300 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
301 PRIVACY_MODE_DISABLED);
302 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
303 EXPECT_EQ(spdy_util_.spdy_version(), session_->GetProtocolVersion());
305 ChunkedUploadDataStream upload_stream(0);
306 const int kFirstChunkSize = kUploadDataSize/2;
307 upload_stream.AppendData(kUploadData, kFirstChunkSize, false);
308 upload_stream.AppendData(kUploadData + kFirstChunkSize,
309 kUploadDataSize - kFirstChunkSize, true);
311 HttpRequestInfo request;
312 request.method = "POST";
313 request.url = GURL("http://www.example.org/");
314 request.upload_data_stream = &upload_stream;
316 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
318 TestCompletionCallback callback;
319 HttpResponseInfo response;
320 HttpRequestHeaders headers;
321 BoundNetLog net_log;
322 SpdyHttpStream http_stream(session_, true);
323 ASSERT_EQ(
325 http_stream.InitializeStream(&request, DEFAULT_PRIORITY,
326 net_log, CompletionCallback()));
328 EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest(
329 headers, &response, callback.callback()));
330 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
332 EXPECT_EQ(OK, callback.WaitForResult());
334 // Because the server closed the connection, we there shouldn't be a session
335 // in the pool anymore.
336 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
339 TEST_P(SpdyHttpStreamTest, ConnectionClosedDuringChunkedPost) {
340 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
342 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
343 scoped_ptr<SpdyFrame> body(
344 framer.CreateDataFrame(1, kUploadData, kUploadDataSize, DATA_FLAG_NONE));
345 MockWrite writes[] = {
346 CreateMockWrite(*req, 0), // Request
347 CreateMockWrite(*body, 1) // First POST upload frame
350 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
351 MockRead reads[] = {
352 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 2) // Server hangs up early.
355 HostPortPair host_port_pair("www.example.org", 80);
356 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
357 PRIVACY_MODE_DISABLED);
358 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
359 EXPECT_EQ(spdy_util_.spdy_version(), session_->GetProtocolVersion());
361 ChunkedUploadDataStream upload_stream(0);
362 // Append first chunk.
363 upload_stream.AppendData(kUploadData, kUploadDataSize, false);
365 HttpRequestInfo request;
366 request.method = "POST";
367 request.url = GURL("http://www.example.org/");
368 request.upload_data_stream = &upload_stream;
370 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
372 TestCompletionCallback callback;
373 HttpResponseInfo response;
374 HttpRequestHeaders headers;
375 BoundNetLog net_log;
376 SpdyHttpStream http_stream(session_, true);
377 ASSERT_EQ(OK, http_stream.InitializeStream(&request, DEFAULT_PRIORITY,
378 net_log, CompletionCallback()));
380 EXPECT_EQ(ERR_IO_PENDING,
381 http_stream.SendRequest(headers, &response, callback.callback()));
382 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
384 EXPECT_EQ(OK, callback.WaitForResult());
386 // Because the server closed the connection, we there shouldn't be a session
387 // in the pool anymore.
388 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
390 // Appending a second chunk now should not result in a crash.
391 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
392 // Appending data is currently done synchronously, but seems best to be
393 // paranoid.
394 base::RunLoop().RunUntilIdle();
397 // Test to ensure the SpdyStream state machine does not get confused when a
398 // chunk becomes available while a write is pending.
399 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPost) {
400 const char kUploadData1[] = "12345678";
401 const int kUploadData1Size = arraysize(kUploadData1)-1;
402 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
403 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false));
404 scoped_ptr<SpdyFrame> chunk2(
405 spdy_util_.ConstructSpdyBodyFrame(
406 1, kUploadData1, kUploadData1Size, false));
407 scoped_ptr<SpdyFrame> chunk3(spdy_util_.ConstructSpdyBodyFrame(1, true));
408 MockWrite writes[] = {
409 CreateMockWrite(*req.get(), 0),
410 CreateMockWrite(*chunk1, 1), // POST upload frames
411 CreateMockWrite(*chunk2, 2),
412 CreateMockWrite(*chunk3, 3),
414 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
415 MockRead reads[] = {
416 CreateMockRead(*resp, 4),
417 CreateMockRead(*chunk1, 5),
418 CreateMockRead(*chunk2, 6),
419 CreateMockRead(*chunk3, 7),
420 MockRead(ASYNC, 0, 8) // EOF
423 HostPortPair host_port_pair("www.example.org", 80);
424 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
425 PRIVACY_MODE_DISABLED);
426 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
428 ChunkedUploadDataStream upload_stream(0);
430 HttpRequestInfo request;
431 request.method = "POST";
432 request.url = GURL("http://www.example.org/");
433 request.upload_data_stream = &upload_stream;
435 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
436 upload_stream.AppendData(kUploadData, kUploadDataSize, false);
438 BoundNetLog net_log;
439 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
440 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
441 net_log, CompletionCallback()));
443 TestCompletionCallback callback;
444 HttpRequestHeaders headers;
445 HttpResponseInfo response;
446 // This will attempt to Write() the initial request and headers, which will
447 // complete asynchronously.
448 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
449 callback.callback()));
450 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
452 // Complete the initial request write and the first chunk.
453 base::RunLoop().RunUntilIdle();
454 ASSERT_TRUE(callback.have_result());
455 EXPECT_EQ(OK, callback.WaitForResult());
457 // Now append the final two chunks which will enqueue two more writes.
458 upload_stream.AppendData(kUploadData1, kUploadData1Size, false);
459 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
461 // Finish writing all the chunks and do all reads.
462 base::RunLoop().RunUntilIdle();
464 // Check response headers.
465 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
467 // Check |chunk1| response.
468 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
469 ASSERT_EQ(kUploadDataSize,
470 http_stream->ReadResponseBody(
471 buf1.get(), kUploadDataSize, callback.callback()));
472 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
474 // Check |chunk2| response.
475 scoped_refptr<IOBuffer> buf2(new IOBuffer(kUploadData1Size));
476 ASSERT_EQ(kUploadData1Size,
477 http_stream->ReadResponseBody(
478 buf2.get(), kUploadData1Size, callback.callback()));
479 EXPECT_EQ(kUploadData1, std::string(buf2->data(), kUploadData1Size));
481 // Check |chunk3| response.
482 scoped_refptr<IOBuffer> buf3(new IOBuffer(kUploadDataSize));
483 ASSERT_EQ(kUploadDataSize,
484 http_stream->ReadResponseBody(
485 buf3.get(), kUploadDataSize, callback.callback()));
486 EXPECT_EQ(kUploadData, std::string(buf3->data(), kUploadDataSize));
488 ASSERT_TRUE(response.headers.get());
489 ASSERT_EQ(200, response.headers->response_code());
492 // Test that the SpdyStream state machine can handle sending a final empty data
493 // frame when uploading a chunked data stream.
494 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithEmptyFinalDataFrame) {
495 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
496 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false));
497 scoped_ptr<SpdyFrame> chunk2(
498 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
499 MockWrite writes[] = {
500 CreateMockWrite(*req.get(), 0),
501 CreateMockWrite(*chunk1, 1), // POST upload frames
502 CreateMockWrite(*chunk2, 2),
504 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
505 MockRead reads[] = {
506 CreateMockRead(*resp, 3),
507 CreateMockRead(*chunk1, 4),
508 CreateMockRead(*chunk2, 5),
509 MockRead(ASYNC, 0, 6) // EOF
512 HostPortPair host_port_pair("www.example.org", 80);
513 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
514 PRIVACY_MODE_DISABLED);
515 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
517 ChunkedUploadDataStream upload_stream(0);
519 HttpRequestInfo request;
520 request.method = "POST";
521 request.url = GURL("http://www.example.org/");
522 request.upload_data_stream = &upload_stream;
524 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
525 upload_stream.AppendData(kUploadData, kUploadDataSize, false);
527 BoundNetLog net_log;
528 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
529 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
530 net_log, CompletionCallback()));
532 TestCompletionCallback callback;
533 HttpRequestHeaders headers;
534 HttpResponseInfo response;
535 // This will attempt to Write() the initial request and headers, which will
536 // complete asynchronously.
537 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
538 callback.callback()));
539 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
541 // Complete the initial request write and the first chunk.
542 base::RunLoop().RunUntilIdle();
543 ASSERT_TRUE(callback.have_result());
544 EXPECT_EQ(OK, callback.WaitForResult());
546 // Now end the stream with an empty data frame and the FIN set.
547 upload_stream.AppendData(NULL, 0, true);
549 // Finish writing the final frame, and perform all reads.
550 base::RunLoop().RunUntilIdle();
552 // Check response headers.
553 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
555 // Check |chunk1| response.
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 // Check |chunk2| response.
563 ASSERT_EQ(0,
564 http_stream->ReadResponseBody(
565 buf1.get(), kUploadDataSize, callback.callback()));
567 ASSERT_TRUE(response.headers.get());
568 ASSERT_EQ(200, response.headers->response_code());
571 // Test that the SpdyStream state machine handles a chunked upload with no
572 // payload. Unclear if this is a case worth supporting.
573 TEST_P(SpdyHttpStreamTest, ChunkedPostWithEmptyPayload) {
574 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
575 scoped_ptr<SpdyFrame> chunk(
576 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
577 MockWrite writes[] = {
578 CreateMockWrite(*req.get(), 0),
579 CreateMockWrite(*chunk, 1),
581 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
582 MockRead reads[] = {
583 CreateMockRead(*resp, 2),
584 CreateMockRead(*chunk, 3),
585 MockRead(ASYNC, 0, 4) // EOF
588 HostPortPair host_port_pair("www.example.org", 80);
589 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
590 PRIVACY_MODE_DISABLED);
591 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
593 ChunkedUploadDataStream upload_stream(0);
595 HttpRequestInfo request;
596 request.method = "POST";
597 request.url = GURL("http://www.example.org/");
598 request.upload_data_stream = &upload_stream;
600 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
601 upload_stream.AppendData("", 0, true);
603 BoundNetLog net_log;
604 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
605 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
606 net_log, CompletionCallback()));
608 TestCompletionCallback callback;
609 HttpRequestHeaders headers;
610 HttpResponseInfo response;
611 // This will attempt to Write() the initial request and headers, which will
612 // complete asynchronously.
613 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
614 callback.callback()));
615 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
617 // Complete writing request, followed by a FIN.
618 base::RunLoop().RunUntilIdle();
619 ASSERT_TRUE(callback.have_result());
620 EXPECT_EQ(OK, callback.WaitForResult());
622 // Check response headers.
623 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
625 // Check |chunk| response.
626 scoped_refptr<IOBuffer> buf(new IOBuffer(1));
627 ASSERT_EQ(0,
628 http_stream->ReadResponseBody(
629 buf.get(), 1, callback.callback()));
631 ASSERT_TRUE(response.headers.get());
632 ASSERT_EQ(200, response.headers->response_code());
635 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
636 TEST_P(SpdyHttpStreamTest, SpdyURLTest) {
637 const char* const full_url = "http://www.example.org/foo?query=what#anchor";
638 const char* const base_url = "http://www.example.org/foo?query=what";
639 scoped_ptr<SpdyFrame> req(
640 spdy_util_.ConstructSpdyGet(base_url, false, 1, LOWEST));
641 MockWrite writes[] = {
642 CreateMockWrite(*req.get(), 0),
644 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
645 MockRead reads[] = {
646 CreateMockRead(*resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF
649 HostPortPair host_port_pair("www.example.org", 80);
650 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
651 PRIVACY_MODE_DISABLED);
652 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
654 HttpRequestInfo request;
655 request.method = "GET";
656 request.url = GURL(full_url);
657 TestCompletionCallback callback;
658 HttpResponseInfo response;
659 HttpRequestHeaders headers;
660 BoundNetLog net_log;
661 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
662 ASSERT_EQ(OK,
663 http_stream->InitializeStream(
664 &request, DEFAULT_PRIORITY, net_log, CompletionCallback()));
666 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
667 callback.callback()));
669 EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec());
671 callback.WaitForResult();
673 // Because we abandoned the stream, we don't expect to find a session in the
674 // pool anymore.
675 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
678 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be
679 // made available is handled correctly.
680 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) {
681 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
682 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, true));
683 MockWrite writes[] = {
684 CreateMockWrite(*req.get(), 0),
685 CreateMockWrite(*chunk1, 1),
687 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
688 scoped_ptr<SpdyFrame> window_update(
689 spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize));
690 MockRead reads[] = {
691 CreateMockRead(*window_update, 2),
692 MockRead(ASYNC, ERR_IO_PENDING, 3),
693 CreateMockRead(*resp, 4),
694 CreateMockRead(*chunk1, 5),
695 MockRead(ASYNC, 0, 6) // EOF
698 HostPortPair host_port_pair("www.example.org", 80);
699 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
700 PRIVACY_MODE_DISABLED);
702 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
704 ChunkedUploadDataStream upload_stream(0);
706 HttpRequestInfo request;
707 request.method = "POST";
708 request.url = GURL("http://www.example.org/");
709 request.upload_data_stream = &upload_stream;
711 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
713 BoundNetLog net_log;
714 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
715 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
716 net_log, CompletionCallback()));
718 HttpRequestHeaders headers;
719 HttpResponseInfo response;
720 // This will attempt to Write() the initial request and headers, which will
721 // complete asynchronously.
722 TestCompletionCallback callback;
723 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
724 callback.callback()));
725 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
727 // Complete the initial request write and first chunk.
728 base::RunLoop().RunUntilIdle();
729 ASSERT_TRUE(callback.have_result());
730 EXPECT_EQ(OK, callback.WaitForResult());
732 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
734 // Verify that the window size has decreased.
735 ASSERT_TRUE(http_stream->stream() != NULL);
736 EXPECT_NE(static_cast<int>(
737 SpdySession::GetDefaultInitialWindowSize(session_->protocol())),
738 http_stream->stream()->send_window_size());
740 // Read window update.
741 base::RunLoop().RunUntilIdle();
743 // Verify the window update.
744 ASSERT_TRUE(http_stream->stream() != NULL);
745 EXPECT_EQ(static_cast<int>(
746 SpdySession::GetDefaultInitialWindowSize(session_->protocol())),
747 http_stream->stream()->send_window_size());
749 // Read rest of data.
750 sequenced_data_->CompleteRead();
751 base::RunLoop().RunUntilIdle();
753 // Check response headers.
754 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
756 // Check |chunk1| response.
757 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
758 ASSERT_EQ(kUploadDataSize,
759 http_stream->ReadResponseBody(
760 buf1.get(), kUploadDataSize, callback.callback()));
761 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
763 ASSERT_TRUE(response.headers.get());
764 ASSERT_EQ(200, response.headers->response_code());
767 // TODO(willchan): Write a longer test for SpdyStream that exercises all
768 // methods.
770 } // namespace net