Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / net / spdy / spdy_http_stream_unittest.cc
blob69ad12c8474946bc4ee29286c4e615c992febce3
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 <stdint.h>
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.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/chunked_upload_data_stream.h"
17 #include "net/base/load_timing_info.h"
18 #include "net/base/load_timing_info_test_util.h"
19 #include "net/base/test_completion_callback.h"
20 #include "net/cert/asn1_util.h"
21 #include "net/http/http_request_info.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/http/http_response_info.h"
24 #include "net/log/test_net_log.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 protected:
76 void TearDown() override {
77 crypto::ECSignatureCreator::SetFactoryForTesting(NULL);
78 base::MessageLoop::current()->RunUntilIdle();
79 EXPECT_TRUE(sequenced_data_->AllReadDataConsumed());
80 EXPECT_TRUE(sequenced_data_->AllWriteDataConsumed());
83 // Initializes the session using SequencedSocketData.
84 void InitSession(MockRead* reads,
85 size_t reads_count,
86 MockWrite* writes,
87 size_t writes_count,
88 const SpdySessionKey& key) {
89 sequenced_data_.reset(
90 new SequencedSocketData(reads, reads_count, writes, writes_count));
91 session_deps_.socket_factory->AddSocketDataProvider(sequenced_data_.get());
92 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
93 session_ = CreateInsecureSpdySession(http_session_, key, BoundNetLog());
96 void TestSendCredentials(
97 ChannelIDService* channel_id_service,
98 const std::string& cert,
99 const std::string& proof);
101 SpdyTestUtil spdy_util_;
102 TestNetLog net_log_;
103 SpdySessionDependencies session_deps_;
104 scoped_ptr<SequencedSocketData> sequenced_data_;
105 scoped_refptr<HttpNetworkSession> http_session_;
106 base::WeakPtr<SpdySession> session_;
108 private:
109 MockECSignatureCreatorFactory ec_signature_creator_factory_;
112 INSTANTIATE_TEST_CASE_P(NextProto,
113 SpdyHttpStreamTest,
114 testing::Values(kProtoSPDY31,
115 kProtoHTTP2));
117 // SpdyHttpStream::GetUploadProgress() should still work even before the
118 // stream is initialized.
119 TEST_P(SpdyHttpStreamTest, GetUploadProgressBeforeInitialization) {
120 MockRead reads[] = {
121 MockRead(ASYNC, 0, 0) // EOF
124 HostPortPair host_port_pair("www.example.org", 80);
125 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
126 PRIVACY_MODE_DISABLED);
127 InitSession(reads, arraysize(reads), NULL, 0, key);
129 SpdyHttpStream stream(session_, false);
130 UploadProgress progress = stream.GetUploadProgress();
131 EXPECT_EQ(0u, progress.size());
132 EXPECT_EQ(0u, progress.position());
134 // Pump the event loop so |reads| is consumed before the function returns.
135 base::RunLoop().RunUntilIdle();
138 TEST_P(SpdyHttpStreamTest, SendRequest) {
139 scoped_ptr<SpdyFrame> req(
140 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
141 MockWrite writes[] = {
142 CreateMockWrite(*req.get(), 0),
144 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
145 MockRead reads[] = {
146 CreateMockRead(*resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF
149 HostPortPair host_port_pair("www.example.org", 80);
150 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
151 PRIVACY_MODE_DISABLED);
152 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
154 HttpRequestInfo request;
155 request.method = "GET";
156 request.url = GURL("http://www.example.org/");
157 TestCompletionCallback callback;
158 HttpResponseInfo response;
159 HttpRequestHeaders headers;
160 BoundNetLog net_log;
161 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
162 // Make sure getting load timing information the stream early does not crash.
163 LoadTimingInfo load_timing_info;
164 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
166 ASSERT_EQ(
168 http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
169 net_log, CompletionCallback()));
170 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
172 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
173 callback.callback()));
174 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
175 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
177 callback.WaitForResult();
179 // Can get timing information once the stream connects.
180 TestLoadTimingNotReused(*http_stream);
182 // Because we abandoned the stream, we don't expect to find a session in the
183 // pool anymore.
184 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
186 TestLoadTimingNotReused(*http_stream);
187 http_stream->Close(true);
188 // Test that there's no crash when trying to get the load timing after the
189 // stream has been closed.
190 TestLoadTimingNotReused(*http_stream);
192 EXPECT_EQ(static_cast<int64_t>(req->size()),
193 http_stream->GetTotalSentBytes());
194 EXPECT_EQ(static_cast<int64_t>(resp->size()),
195 http_stream->GetTotalReceivedBytes());
198 TEST_P(SpdyHttpStreamTest, LoadTimingTwoRequests) {
199 scoped_ptr<SpdyFrame> req1(
200 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
201 scoped_ptr<SpdyFrame> req2(
202 spdy_util_.ConstructSpdyGet(NULL, 0, false, 3, LOWEST, true));
203 MockWrite writes[] = {
204 CreateMockWrite(*req1, 0),
205 CreateMockWrite(*req2, 1),
207 scoped_ptr<SpdyFrame> resp1(
208 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
209 scoped_ptr<SpdyFrame> body1(
210 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
211 scoped_ptr<SpdyFrame> resp2(
212 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
213 scoped_ptr<SpdyFrame> body2(
214 spdy_util_.ConstructSpdyBodyFrame(3, "", 0, true));
215 MockRead reads[] = {
216 CreateMockRead(*resp1, 2),
217 CreateMockRead(*body1, 3),
218 CreateMockRead(*resp2, 4),
219 CreateMockRead(*body2, 5),
220 MockRead(ASYNC, 0, 6) // EOF
223 HostPortPair host_port_pair("www.example.org", 80);
224 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
225 PRIVACY_MODE_DISABLED);
226 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
228 HttpRequestInfo request1;
229 request1.method = "GET";
230 request1.url = GURL("http://www.example.org/");
231 TestCompletionCallback callback1;
232 HttpResponseInfo response1;
233 HttpRequestHeaders headers1;
234 scoped_ptr<SpdyHttpStream> http_stream1(new SpdyHttpStream(session_, true));
236 HttpRequestInfo request2;
237 request2.method = "GET";
238 request2.url = GURL("http://www.example.org/");
239 TestCompletionCallback callback2;
240 HttpResponseInfo response2;
241 HttpRequestHeaders headers2;
242 scoped_ptr<SpdyHttpStream> http_stream2(new SpdyHttpStream(session_, true));
244 // First write.
245 ASSERT_EQ(OK,
246 http_stream1->InitializeStream(&request1, DEFAULT_PRIORITY,
247 BoundNetLog(),
248 CompletionCallback()));
249 EXPECT_EQ(ERR_IO_PENDING, http_stream1->SendRequest(headers1, &response1,
250 callback1.callback()));
251 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
253 EXPECT_LE(0, callback1.WaitForResult());
255 TestLoadTimingNotReused(*http_stream1);
256 LoadTimingInfo load_timing_info1;
257 LoadTimingInfo load_timing_info2;
258 EXPECT_TRUE(http_stream1->GetLoadTimingInfo(&load_timing_info1));
259 EXPECT_FALSE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
261 // Second write.
262 ASSERT_EQ(OK,
263 http_stream2->InitializeStream(&request2, DEFAULT_PRIORITY,
264 BoundNetLog(),
265 CompletionCallback()));
266 EXPECT_EQ(ERR_IO_PENDING, http_stream2->SendRequest(headers2, &response2,
267 callback2.callback()));
268 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
270 EXPECT_LE(0, callback2.WaitForResult());
271 TestLoadTimingReused(*http_stream2);
272 EXPECT_TRUE(http_stream2->GetLoadTimingInfo(&load_timing_info2));
273 EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id);
275 // Read stream 1 to completion, before making sure we can still read load
276 // timing from both streams.
277 scoped_refptr<IOBuffer> buf1(new IOBuffer(1));
278 ASSERT_EQ(
279 0, http_stream1->ReadResponseBody(buf1.get(), 1, callback1.callback()));
281 // Stream 1 has been read to completion.
282 TestLoadTimingNotReused(*http_stream1);
284 EXPECT_EQ(static_cast<int64_t>(req1->size()),
285 http_stream1->GetTotalSentBytes());
286 EXPECT_EQ(static_cast<int64_t>(resp1->size() + body1->size()),
287 http_stream1->GetTotalReceivedBytes());
289 // Stream 2 still has queued body data.
290 TestLoadTimingReused(*http_stream2);
292 EXPECT_EQ(static_cast<int64_t>(req2->size()),
293 http_stream2->GetTotalSentBytes());
294 EXPECT_EQ(static_cast<int64_t>(resp2->size() + body2->size()),
295 http_stream2->GetTotalReceivedBytes());
298 TEST_P(SpdyHttpStreamTest, SendChunkedPost) {
299 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
301 scoped_ptr<SpdyFrame> req(
302 spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
303 scoped_ptr<SpdyFrame> body(
304 framer.CreateDataFrame(1, kUploadData, kUploadDataSize, DATA_FLAG_FIN));
305 MockWrite writes[] = {
306 CreateMockWrite(*req, 0), // request
307 CreateMockWrite(*body, 1) // POST upload frame
310 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
311 MockRead reads[] = {
312 CreateMockRead(*resp, 2),
313 CreateMockRead(*body, 3),
314 MockRead(SYNCHRONOUS, 0, 4) // EOF
317 HostPortPair host_port_pair("www.example.org", 80);
318 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
319 PRIVACY_MODE_DISABLED);
320 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
321 EXPECT_EQ(spdy_util_.spdy_version(), session_->GetProtocolVersion());
323 ChunkedUploadDataStream upload_stream(0);
324 const int kFirstChunkSize = kUploadDataSize/2;
325 upload_stream.AppendData(kUploadData, kFirstChunkSize, false);
326 upload_stream.AppendData(kUploadData + kFirstChunkSize,
327 kUploadDataSize - kFirstChunkSize, true);
329 HttpRequestInfo request;
330 request.method = "POST";
331 request.url = GURL("http://www.example.org/");
332 request.upload_data_stream = &upload_stream;
334 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
336 TestCompletionCallback callback;
337 HttpResponseInfo response;
338 HttpRequestHeaders headers;
339 BoundNetLog net_log;
340 SpdyHttpStream http_stream(session_, true);
341 ASSERT_EQ(
343 http_stream.InitializeStream(&request, DEFAULT_PRIORITY,
344 net_log, CompletionCallback()));
346 EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest(
347 headers, &response, callback.callback()));
348 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
350 EXPECT_EQ(OK, callback.WaitForResult());
352 EXPECT_EQ(static_cast<int64_t>(req->size() + body->size()),
353 http_stream.GetTotalSentBytes());
354 EXPECT_EQ(static_cast<int64_t>(resp->size() + body->size()),
355 http_stream.GetTotalReceivedBytes());
357 // Because the server closed the connection, we there shouldn't be a session
358 // in the pool anymore.
359 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
362 TEST_P(SpdyHttpStreamTest, ConnectionClosedDuringChunkedPost) {
363 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
365 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
366 scoped_ptr<SpdyFrame> body(
367 framer.CreateDataFrame(1, kUploadData, kUploadDataSize, DATA_FLAG_NONE));
368 MockWrite writes[] = {
369 CreateMockWrite(*req, 0), // Request
370 CreateMockWrite(*body, 1) // First POST upload frame
373 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
374 MockRead reads[] = {
375 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 2) // Server hangs up early.
378 HostPortPair host_port_pair("www.example.org", 80);
379 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
380 PRIVACY_MODE_DISABLED);
381 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
382 EXPECT_EQ(spdy_util_.spdy_version(), session_->GetProtocolVersion());
384 ChunkedUploadDataStream upload_stream(0);
385 // Append first chunk.
386 upload_stream.AppendData(kUploadData, kUploadDataSize, false);
388 HttpRequestInfo request;
389 request.method = "POST";
390 request.url = GURL("http://www.example.org/");
391 request.upload_data_stream = &upload_stream;
393 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
395 TestCompletionCallback callback;
396 HttpResponseInfo response;
397 HttpRequestHeaders headers;
398 BoundNetLog net_log;
399 SpdyHttpStream http_stream(session_, true);
400 ASSERT_EQ(OK, http_stream.InitializeStream(&request, DEFAULT_PRIORITY,
401 net_log, CompletionCallback()));
403 EXPECT_EQ(ERR_IO_PENDING,
404 http_stream.SendRequest(headers, &response, callback.callback()));
405 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
407 EXPECT_EQ(OK, callback.WaitForResult());
409 EXPECT_EQ(static_cast<int64_t>(req->size() + body->size()),
410 http_stream.GetTotalSentBytes());
411 EXPECT_EQ(0, http_stream.GetTotalReceivedBytes());
413 // Because the server closed the connection, we there shouldn't be a session
414 // in the pool anymore.
415 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
417 // Appending a second chunk now should not result in a crash.
418 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
419 // Appending data is currently done synchronously, but seems best to be
420 // paranoid.
421 base::RunLoop().RunUntilIdle();
423 // The total sent and received bytes should be unchanged.
424 EXPECT_EQ(static_cast<int64_t>(req->size() + body->size()),
425 http_stream.GetTotalSentBytes());
426 EXPECT_EQ(0, http_stream.GetTotalReceivedBytes());
429 // Test to ensure the SpdyStream state machine does not get confused when a
430 // chunk becomes available while a write is pending.
431 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPost) {
432 const char kUploadData1[] = "12345678";
433 const int kUploadData1Size = arraysize(kUploadData1)-1;
434 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
435 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false));
436 scoped_ptr<SpdyFrame> chunk2(
437 spdy_util_.ConstructSpdyBodyFrame(
438 1, kUploadData1, kUploadData1Size, false));
439 scoped_ptr<SpdyFrame> chunk3(spdy_util_.ConstructSpdyBodyFrame(1, true));
440 MockWrite writes[] = {
441 CreateMockWrite(*req.get(), 0),
442 CreateMockWrite(*chunk1, 1), // POST upload frames
443 CreateMockWrite(*chunk2, 2),
444 CreateMockWrite(*chunk3, 3),
446 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
447 MockRead reads[] = {
448 CreateMockRead(*resp, 4),
449 CreateMockRead(*chunk1, 5),
450 CreateMockRead(*chunk2, 6),
451 CreateMockRead(*chunk3, 7),
452 MockRead(ASYNC, 0, 8) // 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 append the final two chunks which will enqueue two more writes.
490 upload_stream.AppendData(kUploadData1, kUploadData1Size, false);
491 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
493 // Finish writing all the chunks and do all reads.
494 base::RunLoop().RunUntilIdle();
496 EXPECT_EQ(static_cast<int64_t>(req->size() + chunk1->size() + chunk2->size() +
497 chunk3->size()),
498 http_stream->GetTotalSentBytes());
499 EXPECT_EQ(static_cast<int64_t>(resp->size() + chunk1->size() +
500 chunk2->size() + chunk3->size()),
501 http_stream->GetTotalReceivedBytes());
503 // Check response headers.
504 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
506 // Check |chunk1| response.
507 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
508 ASSERT_EQ(kUploadDataSize,
509 http_stream->ReadResponseBody(
510 buf1.get(), kUploadDataSize, callback.callback()));
511 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
513 // Check |chunk2| response.
514 scoped_refptr<IOBuffer> buf2(new IOBuffer(kUploadData1Size));
515 ASSERT_EQ(kUploadData1Size,
516 http_stream->ReadResponseBody(
517 buf2.get(), kUploadData1Size, callback.callback()));
518 EXPECT_EQ(kUploadData1, std::string(buf2->data(), kUploadData1Size));
520 // Check |chunk3| response.
521 scoped_refptr<IOBuffer> buf3(new IOBuffer(kUploadDataSize));
522 ASSERT_EQ(kUploadDataSize,
523 http_stream->ReadResponseBody(
524 buf3.get(), kUploadDataSize, callback.callback()));
525 EXPECT_EQ(kUploadData, std::string(buf3->data(), kUploadDataSize));
527 ASSERT_TRUE(response.headers.get());
528 ASSERT_EQ(200, response.headers->response_code());
531 // Test that the SpdyStream state machine can handle sending a final empty data
532 // frame when uploading a chunked data stream.
533 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithEmptyFinalDataFrame) {
534 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
535 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false));
536 scoped_ptr<SpdyFrame> chunk2(
537 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
538 MockWrite writes[] = {
539 CreateMockWrite(*req.get(), 0),
540 CreateMockWrite(*chunk1, 1), // POST upload frames
541 CreateMockWrite(*chunk2, 2),
543 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
544 MockRead reads[] = {
545 CreateMockRead(*resp, 3),
546 CreateMockRead(*chunk1, 4),
547 CreateMockRead(*chunk2, 5),
548 MockRead(ASYNC, 0, 6) // EOF
551 HostPortPair host_port_pair("www.example.org", 80);
552 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
553 PRIVACY_MODE_DISABLED);
554 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
556 ChunkedUploadDataStream upload_stream(0);
558 HttpRequestInfo request;
559 request.method = "POST";
560 request.url = GURL("http://www.example.org/");
561 request.upload_data_stream = &upload_stream;
563 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
564 upload_stream.AppendData(kUploadData, kUploadDataSize, false);
566 BoundNetLog net_log;
567 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
568 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
569 net_log, CompletionCallback()));
571 TestCompletionCallback callback;
572 HttpRequestHeaders headers;
573 HttpResponseInfo response;
574 // This will attempt to Write() the initial request and headers, which will
575 // complete asynchronously.
576 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
577 callback.callback()));
578 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
580 // Complete the initial request write and the first chunk.
581 base::RunLoop().RunUntilIdle();
582 ASSERT_TRUE(callback.have_result());
583 EXPECT_EQ(OK, callback.WaitForResult());
585 EXPECT_EQ(static_cast<int64_t>(req->size() + chunk1->size()),
586 http_stream->GetTotalSentBytes());
587 EXPECT_EQ(0, http_stream->GetTotalReceivedBytes());
589 // Now end the stream with an empty data frame and the FIN set.
590 upload_stream.AppendData(NULL, 0, true);
592 // Finish writing the final frame, and perform all reads.
593 base::RunLoop().RunUntilIdle();
595 // Check response headers.
596 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
598 EXPECT_EQ(static_cast<int64_t>(req->size() + chunk1->size() + chunk2->size()),
599 http_stream->GetTotalSentBytes());
600 EXPECT_EQ(
601 static_cast<int64_t>(resp->size() + chunk1->size() + chunk2->size()),
602 http_stream->GetTotalReceivedBytes());
604 // Check |chunk1| response.
605 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
606 ASSERT_EQ(kUploadDataSize,
607 http_stream->ReadResponseBody(
608 buf1.get(), kUploadDataSize, callback.callback()));
609 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
611 // Check |chunk2| response.
612 ASSERT_EQ(0,
613 http_stream->ReadResponseBody(
614 buf1.get(), kUploadDataSize, callback.callback()));
616 ASSERT_TRUE(response.headers.get());
617 ASSERT_EQ(200, response.headers->response_code());
620 // Test that the SpdyStream state machine handles a chunked upload with no
621 // payload. Unclear if this is a case worth supporting.
622 TEST_P(SpdyHttpStreamTest, ChunkedPostWithEmptyPayload) {
623 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
624 scoped_ptr<SpdyFrame> chunk(
625 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true));
626 MockWrite writes[] = {
627 CreateMockWrite(*req.get(), 0),
628 CreateMockWrite(*chunk, 1),
630 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
631 MockRead reads[] = {
632 CreateMockRead(*resp, 2),
633 CreateMockRead(*chunk, 3),
634 MockRead(ASYNC, 0, 4) // EOF
637 HostPortPair host_port_pair("www.example.org", 80);
638 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
639 PRIVACY_MODE_DISABLED);
640 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
642 ChunkedUploadDataStream upload_stream(0);
644 HttpRequestInfo request;
645 request.method = "POST";
646 request.url = GURL("http://www.example.org/");
647 request.upload_data_stream = &upload_stream;
649 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
650 upload_stream.AppendData("", 0, true);
652 BoundNetLog net_log;
653 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
654 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
655 net_log, CompletionCallback()));
657 TestCompletionCallback callback;
658 HttpRequestHeaders headers;
659 HttpResponseInfo response;
660 // This will attempt to Write() the initial request and headers, which will
661 // complete asynchronously.
662 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
663 callback.callback()));
664 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
666 // Complete writing request, followed by a FIN.
667 base::RunLoop().RunUntilIdle();
668 ASSERT_TRUE(callback.have_result());
669 EXPECT_EQ(OK, callback.WaitForResult());
671 EXPECT_EQ(static_cast<int64_t>(req->size() + chunk->size()),
672 http_stream->GetTotalSentBytes());
673 EXPECT_EQ(static_cast<int64_t>(resp->size() + chunk->size()),
674 http_stream->GetTotalReceivedBytes());
676 // Check response headers.
677 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
679 // Check |chunk| response.
680 scoped_refptr<IOBuffer> buf(new IOBuffer(1));
681 ASSERT_EQ(0,
682 http_stream->ReadResponseBody(
683 buf.get(), 1, callback.callback()));
685 ASSERT_TRUE(response.headers.get());
686 ASSERT_EQ(200, response.headers->response_code());
689 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
690 TEST_P(SpdyHttpStreamTest, SpdyURLTest) {
691 const char* const full_url = "http://www.example.org/foo?query=what#anchor";
692 const char* const base_url = "http://www.example.org/foo?query=what";
693 scoped_ptr<SpdyFrame> req(
694 spdy_util_.ConstructSpdyGet(base_url, false, 1, LOWEST));
695 MockWrite writes[] = {
696 CreateMockWrite(*req.get(), 0),
698 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
699 MockRead reads[] = {
700 CreateMockRead(*resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF
703 HostPortPair host_port_pair("www.example.org", 80);
704 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
705 PRIVACY_MODE_DISABLED);
706 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
708 HttpRequestInfo request;
709 request.method = "GET";
710 request.url = GURL(full_url);
711 TestCompletionCallback callback;
712 HttpResponseInfo response;
713 HttpRequestHeaders headers;
714 BoundNetLog net_log;
715 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
716 ASSERT_EQ(OK,
717 http_stream->InitializeStream(
718 &request, DEFAULT_PRIORITY, net_log, CompletionCallback()));
720 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
721 callback.callback()));
723 EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec());
725 callback.WaitForResult();
727 EXPECT_EQ(static_cast<int64_t>(req->size()),
728 http_stream->GetTotalSentBytes());
729 EXPECT_EQ(static_cast<int64_t>(resp->size()),
730 http_stream->GetTotalReceivedBytes());
732 // Because we abandoned the stream, we don't expect to find a session in the
733 // pool anymore.
734 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key));
737 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be
738 // made available is handled correctly.
739 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) {
740 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
741 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, true));
742 MockWrite writes[] = {
743 CreateMockWrite(*req.get(), 0),
744 CreateMockWrite(*chunk1, 1),
746 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
747 scoped_ptr<SpdyFrame> window_update(
748 spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize));
749 MockRead reads[] = {
750 CreateMockRead(*window_update, 2),
751 MockRead(ASYNC, ERR_IO_PENDING, 3),
752 CreateMockRead(*resp, 4),
753 CreateMockRead(*chunk1, 5),
754 MockRead(ASYNC, 0, 6) // EOF
757 HostPortPair host_port_pair("www.example.org", 80);
758 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
759 PRIVACY_MODE_DISABLED);
761 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
763 ChunkedUploadDataStream upload_stream(0);
765 HttpRequestInfo request;
766 request.method = "POST";
767 request.url = GURL("http://www.example.org/");
768 request.upload_data_stream = &upload_stream;
770 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
772 BoundNetLog net_log;
773 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
774 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
775 net_log, CompletionCallback()));
777 HttpRequestHeaders headers;
778 HttpResponseInfo response;
779 // This will attempt to Write() the initial request and headers, which will
780 // complete asynchronously.
781 TestCompletionCallback callback;
782 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
783 callback.callback()));
784 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
786 // Complete the initial request write and first chunk.
787 base::RunLoop().RunUntilIdle();
788 ASSERT_TRUE(callback.have_result());
789 EXPECT_EQ(OK, callback.WaitForResult());
791 EXPECT_EQ(static_cast<int64_t>(req->size()),
792 http_stream->GetTotalSentBytes());
793 EXPECT_EQ(0, http_stream->GetTotalReceivedBytes());
795 upload_stream.AppendData(kUploadData, kUploadDataSize, true);
797 // Verify that the window size has decreased.
798 ASSERT_TRUE(http_stream->stream() != NULL);
799 EXPECT_NE(static_cast<int>(
800 SpdySession::GetDefaultInitialWindowSize(session_->protocol())),
801 http_stream->stream()->send_window_size());
803 // Read window update.
804 base::RunLoop().RunUntilIdle();
806 EXPECT_EQ(static_cast<int64_t>(req->size() + chunk1->size()),
807 http_stream->GetTotalSentBytes());
808 // The window update is not counted in the total received bytes.
809 EXPECT_EQ(0, http_stream->GetTotalReceivedBytes());
811 // Verify the window update.
812 ASSERT_TRUE(http_stream->stream() != NULL);
813 EXPECT_EQ(static_cast<int>(
814 SpdySession::GetDefaultInitialWindowSize(session_->protocol())),
815 http_stream->stream()->send_window_size());
817 // Read rest of data.
818 sequenced_data_->CompleteRead();
819 base::RunLoop().RunUntilIdle();
821 EXPECT_EQ(static_cast<int64_t>(req->size() + chunk1->size()),
822 http_stream->GetTotalSentBytes());
823 EXPECT_EQ(static_cast<int64_t>(resp->size() + chunk1->size()),
824 http_stream->GetTotalReceivedBytes());
826 // Check response headers.
827 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
829 // Check |chunk1| response.
830 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
831 ASSERT_EQ(kUploadDataSize,
832 http_stream->ReadResponseBody(
833 buf1.get(), kUploadDataSize, callback.callback()));
834 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
836 ASSERT_TRUE(response.headers.get());
837 ASSERT_EQ(200, response.headers->response_code());
840 // TODO(willchan): Write a longer test for SpdyStream that exercises all
841 // methods.
843 } // namespace net