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