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.
4
5#include "net/spdy/spdy_http_stream.h"
6
7#include <vector>
8
9#include "base/memory/scoped_ptr.h"
10#include "base/message_loop/message_loop_proxy.h"
11#include "base/run_loop.h"
12#include "base/stl_util.h"
13#include "crypto/ec_private_key.h"
14#include "crypto/ec_signature_creator.h"
15#include "crypto/signature_creator.h"
16#include "net/base/capturing_net_log.h"
17#include "net/base/load_timing_info.h"
18#include "net/base/load_timing_info_test_util.h"
19#include "net/base/upload_data_stream.h"
20#include "net/base/upload_element_reader.h"
21#include "net/cert/asn1_util.h"
22#include "net/http/http_request_info.h"
23#include "net/http/http_response_headers.h"
24#include "net/http/http_response_info.h"
25#include "net/socket/next_proto.h"
26#include "net/socket/socket_test_util.h"
27#include "net/spdy/spdy_http_utils.h"
28#include "net/spdy/spdy_session.h"
29#include "net/spdy/spdy_test_util_common.h"
30#include "net/ssl/default_channel_id_store.h"
31#include "testing/gtest/include/gtest/gtest.h"
32
33namespace net {
34
35namespace {
36
37// Tests the load timing of a stream that's connected and is not the first
38// request sent on a connection.
39void TestLoadTimingReused(const HttpStream& stream) {
40  LoadTimingInfo load_timing_info;
41  EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info));
42
43  EXPECT_TRUE(load_timing_info.socket_reused);
44  EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id);
45
46  ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
47  ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
48}
49
50// Tests the load timing of a stream that's connected and using a fresh
51// connection.
52void TestLoadTimingNotReused(const HttpStream& stream) {
53  LoadTimingInfo load_timing_info;
54  EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info));
55
56  EXPECT_FALSE(load_timing_info.socket_reused);
57  EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id);
58
59  ExpectConnectTimingHasTimes(load_timing_info.connect_timing,
60                              CONNECT_TIMING_HAS_DNS_TIMES);
61  ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
62}
63
64}  // namespace
65
66class 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_;
73  }
74
75  DeterministicSocketData* deterministic_data() {
76    return deterministic_data_.get();
77  }
78
79  OrderedSocketData* data() { return data_.get(); }
80
81 protected:
82  virtual void TearDown() OVERRIDE {
83    crypto::ECSignatureCreator::SetFactoryForTesting(NULL);
84    base::MessageLoop::current()->RunUntilIdle();
85  }
86
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());
100  }
101
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());
111  }
112
113  void TestSendCredentials(
114    ChannelIDService* channel_id_service,
115    const std::string& cert,
116    const std::string& proof);
117
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_;
125
126 private:
127  MockECSignatureCreatorFactory ec_signature_creator_factory_;
128};
129
130INSTANTIATE_TEST_CASE_P(
131    NextProto,
132    SpdyHttpStreamTest,
133    testing::Values(kProtoDeprecatedSPDY2,
134                    kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
135
136// SpdyHttpStream::GetUploadProgress() should still work even before the
137// stream is initialized.
138TEST_P(SpdyHttpStreamTest, GetUploadProgressBeforeInitialization) {
139  MockRead reads[] = {
140    MockRead(ASYNC, 0, 0)  // EOF
141  };
142
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);
147
148  SpdyHttpStream stream(session_, false);
149  UploadProgress progress = stream.GetUploadProgress();
150  EXPECT_EQ(0u, progress.size());
151  EXPECT_EQ(0u, progress.position());
152
153  // Pump the event loop so |reads| is consumed before the function returns.
154  base::RunLoop().RunUntilIdle();
155}
156
157TEST_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),
162  };
163  scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
164  MockRead reads[] = {
165    CreateMockRead(*resp, 2),
166    MockRead(SYNCHRONOUS, 0, 3)  // EOF
167  };
168
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);
173
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));
185
186  ASSERT_EQ(
187      OK,
188      http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
189                                    net_log, CompletionCallback()));
190  EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info));
191
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));
196
197  // This triggers the MockWrite and read 2
198  callback.WaitForResult();
199
200  // Can get timing information once the stream connects.
201  TestLoadTimingNotReused(*http_stream);
202
203  // This triggers read 3. The empty read causes the session to shut down.
204  data()->CompleteRead();
205
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());
211
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);
217}
218
219TEST_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),
227  };
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
242  };
243
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);
250
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));
258
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));
266
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));
275
276  deterministic_data()->RunFor(1);
277  EXPECT_LE(0, callback1.WaitForResult());
278
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));
284
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));
293
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);
299
300  // All the reads.
301  deterministic_data()->RunFor(6);
302
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()));
308
309  // Stream 1 has been read to completion.
310  TestLoadTimingNotReused(*http_stream1);
311  // Stream 2 still has queued body data.
312  TestLoadTimingReused(*http_stream2);
313}
314
315TEST_P(SpdyHttpStreamTest, SendChunkedPost) {
316  BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
317
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
326
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
332
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());
340
341  UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0);
342  const int kFirstChunkSize = kUploadDataSize/2;
343  upload_stream.AppendChunk(kUploadData, kFirstChunkSize, false);
344  upload_stream.AppendChunk(kUploadData + kFirstChunkSize,
345                            kUploadDataSize - kFirstChunkSize, true);
346
347  HttpRequestInfo request;
348  request.method = "POST";
349  request.url = GURL("http://www.google.com/");
350  request.upload_data_stream = &upload_stream;
351
352  ASSERT_EQ(OK, upload_stream.Init(CompletionCallback()));
353
354  TestCompletionCallback callback;
355  HttpResponseInfo response;
356  HttpRequestHeaders headers;
357  BoundNetLog net_log;
358  SpdyHttpStream http_stream(session_, true);
359  ASSERT_EQ(
360      OK,
361      http_stream.InitializeStream(&request, DEFAULT_PRIORITY,
362                                   net_log, CompletionCallback()));
363
364  EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest(
365      headers, &response, callback.callback()));
366  EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
367
368  // This results in writing the post body and reading the response headers.
369  callback.WaitForResult();
370
371  // This triggers reading the body and the EOF, causing the session to shut
372  // down.
373  data()->CompleteRead();
374  base::MessageLoop::current()->RunUntilIdle();
375
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());
381}
382
383// Test to ensure the SpdyStream state machine does not get confused when a
384// chunk becomes available while a write is pending.
385TEST_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),
399  };
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
407  };
408
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);
415
416  UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0);
417
418  HttpRequestInfo request;
419  request.method = "POST";
420  request.url = GURL("http://www.google.com/");
421  request.upload_data_stream = &upload_stream;
422
423  ASSERT_EQ(OK, upload_stream.Init(CompletionCallback()));
424  upload_stream.AppendChunk(kUploadData, kUploadDataSize, false);
425
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()));
430
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));
439
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());
444
445  // Now append the final two chunks which will enqueue two more writes.
446  upload_stream.AppendChunk(kUploadData1, kUploadData1Size, false);
447  upload_stream.AppendChunk(kUploadData, kUploadDataSize, true);
448
449  // Finish writing all the chunks.
450  deterministic_data()->RunFor(2);
451
452  // Read response headers.
453  deterministic_data()->RunFor(1);
454  ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
455
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));
463
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));
471
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));
479
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());
486}
487
488// Test that the SpdyStream state machine can handle sending a final empty data
489// frame when uploading a chunked data stream.
490TEST_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),
499  };
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
506  };
507
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);
514
515  UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0);
516
517  HttpRequestInfo request;
518  request.method = "POST";
519  request.url = GURL("http://www.google.com/");
520  request.upload_data_stream = &upload_stream;
521
522  ASSERT_EQ(OK, upload_stream.Init(CompletionCallback()));
523  upload_stream.AppendChunk(kUploadData, kUploadDataSize, false);
524
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()));
529
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));
538
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());
543
544  // Now end the stream with an empty data frame and the FIN set.
545  upload_stream.AppendChunk(NULL, 0, true);
546
547  // Finish writing the final frame.
548  deterministic_data()->RunFor(1);
549
550  // Read response headers.
551  deterministic_data()->RunFor(1);
552  ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
553
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));
561
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()));
567
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());
574}
575
576// Test that the SpdyStream state machine handles a chunked upload with no
577// payload. Unclear if this is a case worth supporting.
578TEST_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),
585  };
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
591  };
592
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);
599
600  UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0);
601
602  HttpRequestInfo request;
603  request.method = "POST";
604  request.url = GURL("http://www.google.com/");
605  request.upload_data_stream = &upload_stream;
606
607  ASSERT_EQ(OK, upload_stream.Init(CompletionCallback()));
608  upload_stream.AppendChunk("", 0, true);
609
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()));
614
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));
623
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());
628
629  // Read response headers.
630  deterministic_data()->RunFor(1);
631  ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
632
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()));
639
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());
646}
647
648// Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
649TEST_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),
656  };
657  scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
658  MockRead reads[] = {
659    CreateMockRead(*resp, 2),
660    MockRead(SYNCHRONOUS, 0, 3)  // EOF
661  };
662
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);
667
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()));
679
680  EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
681                                                     callback.callback()));
682
683  EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec());
684
685  // This triggers the MockWrite and read 2
686  callback.WaitForResult();
687
688  // This triggers read 3. The empty read causes the session to shut down.
689  data()->CompleteRead();
690
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());
696}
697
698// The tests below are only for SPDY/3 and above.
699
700// Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be
701// made available is handled correctly.
702TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) {
703  if (GetParam() < kProtoSPDY3)
704    return;
705
706  scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
707  scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, true));
708  MockWrite writes[] = {
709    CreateMockWrite(*req.get(), 0),
710    CreateMockWrite(*chunk1, 1),
711  };
712  scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
713  scoped_ptr<SpdyFrame> window_update(
714      spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize));
715  MockRead reads[] = {
716    CreateMockRead(*window_update, 2),
717    CreateMockRead(*resp, 3),
718    CreateMockRead(*chunk1, 4),
719    MockRead(ASYNC, 0, 5)  // EOF
720  };
721
722  HostPortPair host_port_pair("www.google.com", 80);
723  SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
724                     PRIVACY_MODE_DISABLED);
725
726  InitSessionDeterministic(reads, arraysize(reads),
727                           writes, arraysize(writes),
728                           key);
729
730  UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0);
731
732  HttpRequestInfo request;
733  request.method = "POST";
734  request.url = GURL("http://www.google.com/");
735  request.upload_data_stream = &upload_stream;
736
737  ASSERT_EQ(OK, upload_stream.Init(CompletionCallback()));
738  upload_stream.AppendChunk(kUploadData, kUploadDataSize, true);
739
740  BoundNetLog net_log;
741  scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
742  ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY,
743                                              net_log, CompletionCallback()));
744
745  HttpRequestHeaders headers;
746  HttpResponseInfo response;
747  // This will attempt to Write() the initial request and headers, which will
748  // complete asynchronously.
749  TestCompletionCallback callback;
750  EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response,
751                                                     callback.callback()));
752  EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key));
753
754  // Complete the initial request write and first chunk.
755  deterministic_data_->RunFor(2);
756  ASSERT_TRUE(callback.have_result());
757  EXPECT_EQ(OK, callback.WaitForResult());
758
759  // Verify that the window size has decreased.
760  ASSERT_TRUE(http_stream->stream() != NULL);
761  EXPECT_NE(static_cast<int>(kSpdyStreamInitialWindowSize),
762            http_stream->stream()->send_window_size());
763
764  // Read window update.
765  deterministic_data_->RunFor(1);
766
767  // Verify the window update.
768  ASSERT_TRUE(http_stream->stream() != NULL);
769  EXPECT_EQ(static_cast<int>(kSpdyStreamInitialWindowSize),
770            http_stream->stream()->send_window_size());
771
772  // Read response headers.
773  deterministic_data_->RunFor(1);
774  ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback()));
775
776  // Read and check |chunk1| response.
777  deterministic_data_->RunFor(1);
778  scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize));
779  ASSERT_EQ(kUploadDataSize,
780            http_stream->ReadResponseBody(
781                buf1.get(), kUploadDataSize, callback.callback()));
782  EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
783
784  // Finish reading the |EOF|.
785  deterministic_data_->RunFor(1);
786  ASSERT_TRUE(response.headers.get());
787  ASSERT_EQ(200, response.headers->response_code());
788  EXPECT_TRUE(deterministic_data_->at_read_eof());
789  EXPECT_TRUE(deterministic_data_->at_write_eof());
790}
791
792// TODO(willchan): Write a longer test for SpdyStream that exercises all
793// methods.
794
795}  // namespace net
796