Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / spdy / spdy_http_stream_unittest.cc
blob691f38985ad8ab8c83cb0241ca24a96dcd5b6065
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_14,
114 kProtoHTTP2));
116 // SpdyHttpStream::GetUploadProgress() should still work even before the
117 // stream is initialized.
118 TEST_P(SpdyHttpStreamTest, GetUploadProgressBeforeInitialization) {
119 MockRead reads[] = {
120 MockRead(ASYNC, 0, 0) // EOF
123 HostPortPair host_port_pair("www.example.org", 80);
124 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
125 PRIVACY_MODE_DISABLED);
126 InitSession(reads, arraysize(reads), NULL, 0, key);
128 SpdyHttpStream stream(session_, false);
129 UploadProgress progress = stream.GetUploadProgress();
130 EXPECT_EQ(0u, progress.size());
131 EXPECT_EQ(0u, progress.position());
133 // Pump the event loop so |reads| is consumed before the function returns.
134 base::RunLoop().RunUntilIdle();
137 TEST_P(SpdyHttpStreamTest, SendRequest) {
138 scoped_ptr<SpdyFrame> req(
139 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
140 MockWrite writes[] = {
141 CreateMockWrite(*req.get(), 0),
143 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
144 MockRead reads[] = {
145 CreateMockRead(*resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF
148 HostPortPair host_port_pair("www.example.org", 80);
149 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
150 PRIVACY_MODE_DISABLED);
151 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
153 HttpRequestInfo request;
154 request.method = "GET";
155 request.url = GURL("http://www.example.org/");
156 TestCompletionCallback callback;
157 HttpResponseInfo response;
158 HttpRequestHeaders headers;
159 BoundNetLog net_log;
160 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
161 // Make sure getting load timing information the stream early does not crash.
162 LoadTimingInfo load_timing_info;
163 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
165 ASSERT_EQ(
167 http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
168 net_log, CompletionCallback()));
169 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
171 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
172 callback.callback()));
173 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
174 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
176 callback.WaitForResult();
178 // Can get timing information once the stream connects.
179 TestLoadTimingNotReused(*http_stream);
181 // Because we abandoned the stream, we don't expect to find a session in the
182 // pool anymore.
183 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
185 TestLoadTimingNotReused(*http_stream);
186 http_stream->Close(true);
187 // Test that there's no crash when trying to get the load timing after the
188 // stream has been closed.
189 TestLoadTimingNotReused(*http_stream);
192 TEST_P(SpdyHttpStreamTest, LoadTimingTwoRequests) {
193 scoped_ptr<SpdyFrame> req1(
194 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
195 scoped_ptr<SpdyFrame> req2(
196 spdy_util_.ConstructSpdyGet(NULL, 0, false, 3, LOWEST, true));
197 MockWrite writes[] = {
198 CreateMockWrite(*req1, 0),
199 CreateMockWrite(*req2, 1),
201 scoped_ptr<SpdyFrame> resp1(
202 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
203 scoped_ptr<SpdyFrame> body1(
204 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
205 scoped_ptr<SpdyFrame> resp2(
206 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
207 scoped_ptr<SpdyFrame> body2(
208 spdy_util_.ConstructSpdyBodyFrame(3, "", 0, true));
209 MockRead reads[] = {
210 CreateMockRead(*resp1, 2),
211 CreateMockRead(*body1, 3),
212 CreateMockRead(*resp2, 4),
213 CreateMockRead(*body2, 5),
214 MockRead(ASYNC, 0, 6) // EOF
217 HostPortPair host_port_pair("www.example.org", 80);
218 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
219 PRIVACY_MODE_DISABLED);
220 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
222 HttpRequestInfo request1;
223 request1.method = "GET";
224 request1.url = GURL("http://www.example.org/");
225 TestCompletionCallback callback1;
226 HttpResponseInfo response1;
227 HttpRequestHeaders headers1;
228 scoped_ptr<SpdyHttpStream> http_stream1(new SpdyHttpStream(session_, true));
230 HttpRequestInfo request2;
231 request2.method = "GET";
232 request2.url = GURL("http://www.example.org/");
233 TestCompletionCallback callback2;
234 HttpResponseInfo response2;
235 HttpRequestHeaders headers2;
236 scoped_ptr<SpdyHttpStream> http_stream2(new SpdyHttpStream(session_, true));
238 // First write.
239 ASSERT_EQ(OK,
240 http_stream1->InitializeStream(&request1, DEFAULT_PRIORITY,
241 BoundNetLog(),
242 CompletionCallback()));
243 EXPECT_EQ(ERR_IO_PENDING, http_stream1->SendRequest(headers1, &response1,
244 callback1.callback()));
245 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
247 EXPECT_LE(0, callback1.WaitForResult());
249 TestLoadTimingNotReused(*http_stream1);
250 LoadTimingInfo load_timing_info1;
251 LoadTimingInfo load_timing_info2;
252 EXPECT_TRUE(http_stream1->GetLoadTimingInfo(&load_timing_info1));
253 EXPECT_FALSE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
255 // Second write.
256 ASSERT_EQ(OK,
257 http_stream2->InitializeStream(&request2, DEFAULT_PRIORITY,
258 BoundNetLog(),
259 CompletionCallback()));
260 EXPECT_EQ(ERR_IO_PENDING, http_stream2->SendRequest(headers2, &response2,
261 callback2.callback()));
262 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
264 EXPECT_LE(0, callback2.WaitForResult());
265 TestLoadTimingReused(*http_stream2);
266 EXPECT_TRUE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
267 EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id);
269 // Read stream 1 to completion, before making sure we can still read load
270 // timing from both streams.
271 scoped_refptr<IOBuffer> buf1(new IOBuffer(1));
272 ASSERT_EQ(
273 0, http_stream1->ReadResponseBody(buf1.get(), 1, callback1.callback()));
275 // Stream 1 has been read to completion.
276 TestLoadTimingNotReused(*http_stream1);
277 // Stream 2 still has queued body data.
278 TestLoadTimingReused(*http_stream2);
281 TEST_P(SpdyHttpStreamTest, SendChunkedPost) {
282 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
284 scoped_ptr<SpdyFrame> req(
285 spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
286 scoped_ptr<SpdyFrame> body(
287 framer.CreateDataFrame(1, kUploadData, kUploadDataSize, DATA_FLAG_FIN));
288 MockWrite writes[] = {
289 CreateMockWrite(*req, 0), // request
290 CreateMockWrite(*body, 1) // POST upload frame
293 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
294 MockRead reads[] = {
295 CreateMockRead(*resp, 2),
296 CreateMockRead(*body, 3),
297 MockRead(SYNCHRONOUS, 0, 4) // EOF
300 HostPortPair host_port_pair("www.example.org", 80);
301 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
302 PRIVACY_MODE_DISABLED);
303 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
304 EXPECT_EQ(spdy_util_.spdy_version(), session_->GetProtocolVersion());
306 ChunkedUploadDataStream upload_stream(0);
307 const int kFirstChunkSize = kUploadDataSize/2;
308 upload_stream.AppendData(kUploadData, kFirstChunkSize, false);
309 upload_stream.AppendData(kUploadData + kFirstChunkSize,
310 kUploadDataSize - kFirstChunkSize, true);
312 HttpRequestInfo request;
313 request.method = "POST";
314 request.url = GURL("http://www.example.org/");
315 request.upload_data_stream = &upload_stream;
317 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
319 TestCompletionCallback callback;
320 HttpResponseInfo response;
321 HttpRequestHeaders headers;
322 BoundNetLog net_log;
323 SpdyHttpStream http_stream(session_, true);
324 ASSERT_EQ(
326 http_stream.InitializeStream(&request, DEFAULT_PRIORITY,
327 net_log, CompletionCallback()));
329 EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest(
330 headers, &response, callback.callback()));
331 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
333 callback.WaitForResult();
335 // Because we abandoned the stream, we don't expect to find a session in the
336 // pool anymore.
337 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
340 // Test to ensure the SpdyStream state machine does not get confused when a
341 // chunk becomes available while a write is pending.
342 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPost) {
343 const char kUploadData1[] = "12345678";
344 const int kUploadData1Size = arraysize(kUploadData1)-1;
345 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
346 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false));
347 scoped_ptr<SpdyFrame> chunk2(
348 spdy_util_.ConstructSpdyBodyFrame(
349 1, kUploadData1, kUploadData1Size, false));
350 scoped_ptr<SpdyFrame> chunk3(spdy_util_.ConstructSpdyBodyFrame(1, true));
351 MockWrite writes[] = {
352 CreateMockWrite(*req.get(), 0),
353 CreateMockWrite(*chunk1, 1), // POST upload frames
354 CreateMockWrite(*chunk2, 2),
355 CreateMockWrite(*chunk3, 3),
357 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
358 MockRead reads[] = {
359 CreateMockRead(*resp, 4),
360 CreateMockRead(*chunk1, 5),
361 CreateMockRead(*chunk2, 6),
362 CreateMockRead(*chunk3, 7),
363 MockRead(ASYNC, 0, 8) // EOF
366 HostPortPair host_port_pair("www.example.org", 80);
367 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
368 PRIVACY_MODE_DISABLED);
369 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
371 ChunkedUploadDataStream upload_stream(0);
373 HttpRequestInfo request;
374 request.method = "POST";
375 request.url = GURL("http://www.example.org/");
376 request.upload_data_stream = &upload_stream;
378 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
379 upload_stream.AppendData(kUploadData, kUploadDataSize, false);
381 BoundNetLog net_log;
382 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
383 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
384 net_log, CompletionCallback()));
386 TestCompletionCallback callback;
387 HttpRequestHeaders headers;
388 HttpResponseInfo response;
389 // This will attempt to Write() the initial request and headers, which will
390 // complete asynchronously.
391 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
392 callback.callback()));
393 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
395 // Complete the initial request write and the first chunk.
396 base::RunLoop().RunUntilIdle();
397 ASSERT_TRUE(callback.have_result());
398 EXPECT_EQ(OK, callback.WaitForResult());
400 // Now append the final two chunks which will enqueue two more writes.
401 upload_stream.AppendData(kUploadData1, kUploadData1Size, false);
402 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
404 // Finish writing all the chunks and do all reads.
405 base::RunLoop().RunUntilIdle();
407 // Check response headers.
408 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
410 // Check |chunk1| response.
411 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
412 ASSERT_EQ(kUploadDataSize,
413 http_stream->ReadResponseBody(
414 buf1.get(), kUploadDataSize, callback.callback()));
415 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
417 // Check |chunk2| response.
418 scoped_refptr<IOBuffer> buf2(new IOBuffer(kUploadData1Size));
419 ASSERT_EQ(kUploadData1Size,
420 http_stream->ReadResponseBody(
421 buf2.get(), kUploadData1Size, callback.callback()));
422 EXPECT_EQ(kUploadData1, std::string(buf2->data(), kUploadData1Size));
424 // Check |chunk3| response.
425 scoped_refptr<IOBuffer> buf3(new IOBuffer(kUploadDataSize));
426 ASSERT_EQ(kUploadDataSize,
427 http_stream->ReadResponseBody(
428 buf3.get(), kUploadDataSize, callback.callback()));
429 EXPECT_EQ(kUploadData, std::string(buf3->data(), kUploadDataSize));
431 ASSERT_TRUE(response.headers.get());
432 ASSERT_EQ(200, response.headers->response_code());
435 // Test that the SpdyStream state machine can handle sending a final empty data
436 // frame when uploading a chunked data stream.
437 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithEmptyFinalDataFrame) {
438 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
439 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false));
440 scoped_ptr<SpdyFrame> chunk2(
441 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
442 MockWrite writes[] = {
443 CreateMockWrite(*req.get(), 0),
444 CreateMockWrite(*chunk1, 1), // POST upload frames
445 CreateMockWrite(*chunk2, 2),
447 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
448 MockRead reads[] = {
449 CreateMockRead(*resp, 3),
450 CreateMockRead(*chunk1, 4),
451 CreateMockRead(*chunk2, 5),
452 MockRead(ASYNC, 0, 6) // EOF
455 HostPortPair host_port_pair("www.example.org", 80);
456 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
457 PRIVACY_MODE_DISABLED);
458 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
460 ChunkedUploadDataStream upload_stream(0);
462 HttpRequestInfo request;
463 request.method = "POST";
464 request.url = GURL("http://www.example.org/");
465 request.upload_data_stream = &upload_stream;
467 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
468 upload_stream.AppendData(kUploadData, kUploadDataSize, false);
470 BoundNetLog net_log;
471 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
472 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
473 net_log, CompletionCallback()));
475 TestCompletionCallback callback;
476 HttpRequestHeaders headers;
477 HttpResponseInfo response;
478 // This will attempt to Write() the initial request and headers, which will
479 // complete asynchronously.
480 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
481 callback.callback()));
482 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
484 // Complete the initial request write and the first chunk.
485 base::RunLoop().RunUntilIdle();
486 ASSERT_TRUE(callback.have_result());
487 EXPECT_EQ(OK, callback.WaitForResult());
489 // Now end the stream with an empty data frame and the FIN set.
490 upload_stream.AppendData(NULL, 0, true);
492 // Finish writing the final frame, and perform all reads.
493 base::RunLoop().RunUntilIdle();
495 // Check response headers.
496 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
498 // Check |chunk1| response.
499 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
500 ASSERT_EQ(kUploadDataSize,
501 http_stream->ReadResponseBody(
502 buf1.get(), kUploadDataSize, callback.callback()));
503 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
505 // Check |chunk2| response.
506 ASSERT_EQ(0,
507 http_stream->ReadResponseBody(
508 buf1.get(), kUploadDataSize, callback.callback()));
510 ASSERT_TRUE(response.headers.get());
511 ASSERT_EQ(200, response.headers->response_code());
514 // Test that the SpdyStream state machine handles a chunked upload with no
515 // payload. Unclear if this is a case worth supporting.
516 TEST_P(SpdyHttpStreamTest, ChunkedPostWithEmptyPayload) {
517 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
518 scoped_ptr<SpdyFrame> chunk(
519 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
520 MockWrite writes[] = {
521 CreateMockWrite(*req.get(), 0),
522 CreateMockWrite(*chunk, 1),
524 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
525 MockRead reads[] = {
526 CreateMockRead(*resp, 2),
527 CreateMockRead(*chunk, 3),
528 MockRead(ASYNC, 0, 4) // EOF
531 HostPortPair host_port_pair("www.example.org", 80);
532 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
533 PRIVACY_MODE_DISABLED);
534 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
536 ChunkedUploadDataStream upload_stream(0);
538 HttpRequestInfo request;
539 request.method = "POST";
540 request.url = GURL("http://www.example.org/");
541 request.upload_data_stream = &upload_stream;
543 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
544 upload_stream.AppendData("", 0, true);
546 BoundNetLog net_log;
547 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
548 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
549 net_log, CompletionCallback()));
551 TestCompletionCallback callback;
552 HttpRequestHeaders headers;
553 HttpResponseInfo response;
554 // This will attempt to Write() the initial request and headers, which will
555 // complete asynchronously.
556 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
557 callback.callback()));
558 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
560 // Complete writing request, followed by a FIN.
561 base::RunLoop().RunUntilIdle();
562 ASSERT_TRUE(callback.have_result());
563 EXPECT_EQ(OK, callback.WaitForResult());
565 // Check response headers.
566 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
568 // Check |chunk| response.
569 scoped_refptr<IOBuffer> buf(new IOBuffer(1));
570 ASSERT_EQ(0,
571 http_stream->ReadResponseBody(
572 buf.get(), 1, callback.callback()));
574 ASSERT_TRUE(response.headers.get());
575 ASSERT_EQ(200, response.headers->response_code());
578 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
579 TEST_P(SpdyHttpStreamTest, SpdyURLTest) {
580 const char* const full_url = "http://www.example.org/foo?query=what#anchor";
581 const char* const base_url = "http://www.example.org/foo?query=what";
582 scoped_ptr<SpdyFrame> req(
583 spdy_util_.ConstructSpdyGet(base_url, false, 1, LOWEST));
584 MockWrite writes[] = {
585 CreateMockWrite(*req.get(), 0),
587 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
588 MockRead reads[] = {
589 CreateMockRead(*resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF
592 HostPortPair host_port_pair("www.example.org", 80);
593 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
594 PRIVACY_MODE_DISABLED);
595 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
597 HttpRequestInfo request;
598 request.method = "GET";
599 request.url = GURL(full_url);
600 TestCompletionCallback callback;
601 HttpResponseInfo response;
602 HttpRequestHeaders headers;
603 BoundNetLog net_log;
604 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
605 ASSERT_EQ(OK,
606 http_stream->InitializeStream(
607 &request, DEFAULT_PRIORITY, net_log, CompletionCallback()));
609 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
610 callback.callback()));
612 EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec());
614 callback.WaitForResult();
616 // Because we abandoned the stream, we don't expect to find a session in the
617 // pool anymore.
618 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
621 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be
622 // made available is handled correctly.
623 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) {
624 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
625 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, true));
626 MockWrite writes[] = {
627 CreateMockWrite(*req.get(), 0),
628 CreateMockWrite(*chunk1, 1),
630 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
631 scoped_ptr<SpdyFrame> window_update(
632 spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize));
633 MockRead reads[] = {
634 CreateMockRead(*window_update, 2),
635 MockRead(ASYNC, ERR_IO_PENDING, 3),
636 CreateMockRead(*resp, 4),
637 CreateMockRead(*chunk1, 5),
638 MockRead(ASYNC, 0, 6) // EOF
641 HostPortPair host_port_pair("www.example.org", 80);
642 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
643 PRIVACY_MODE_DISABLED);
645 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
647 ChunkedUploadDataStream upload_stream(0);
649 HttpRequestInfo request;
650 request.method = "POST";
651 request.url = GURL("http://www.example.org/");
652 request.upload_data_stream = &upload_stream;
654 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
656 BoundNetLog net_log;
657 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
658 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
659 net_log, CompletionCallback()));
661 HttpRequestHeaders headers;
662 HttpResponseInfo response;
663 // This will attempt to Write() the initial request and headers, which will
664 // complete asynchronously.
665 TestCompletionCallback callback;
666 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
667 callback.callback()));
668 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
670 // Complete the initial request write and first chunk.
671 base::RunLoop().RunUntilIdle();
672 ASSERT_TRUE(callback.have_result());
673 EXPECT_EQ(OK, callback.WaitForResult());
675 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
677 // Verify that the window size has decreased.
678 ASSERT_TRUE(http_stream->stream() != NULL);
679 EXPECT_NE(static_cast<int>(
680 SpdySession::GetDefaultInitialWindowSize(session_->protocol())),
681 http_stream->stream()->send_window_size());
683 // Read window update.
684 base::RunLoop().RunUntilIdle();
686 // Verify the window update.
687 ASSERT_TRUE(http_stream->stream() != NULL);
688 EXPECT_EQ(static_cast<int>(
689 SpdySession::GetDefaultInitialWindowSize(session_->protocol())),
690 http_stream->stream()->send_window_size());
692 // Read rest of data.
693 sequenced_data_->CompleteRead();
694 base::RunLoop().RunUntilIdle();
696 // Check response headers.
697 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
699 // Check |chunk1| response.
700 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
701 ASSERT_EQ(kUploadDataSize,
702 http_stream->ReadResponseBody(
703 buf1.get(), kUploadDataSize, callback.callback()));
704 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
706 ASSERT_TRUE(response.headers.get());
707 ASSERT_EQ(200, response.headers->response_code());
710 // TODO(willchan): Write a longer test for SpdyStream that exercises all
711 // methods.
713 } // namespace net