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 <cmath> | |
6 #include <memory> | |
7 #include <utility> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/bind_helpers.h" | |
12 #include "base/files/file_util.h" | |
13 #include "base/files/scoped_temp_dir.h" | |
14 #include "base/memory/ptr_util.h" | |
15 #include "base/run_loop.h" | |
16 #include "base/test/test_file_util.h" | |
17 #include "base/threading/thread_task_runner_handle.h" | |
18 #include "net/base/auth.h" | |
19 #include "net/base/chunked_upload_data_stream.h" | |
20 #include "net/base/elements_upload_data_stream.h" | |
21 #include "net/base/proxy_delegate.h" | |
22 #include "net/base/request_priority.h" | |
23 #include "net/base/test_proxy_delegate.h" | |
24 #include "net/base/upload_bytes_element_reader.h" | |
25 #include "net/base/upload_file_element_reader.h" | |
26 #include "net/http/http_auth_scheme.h" | |
27 #include "net/http/http_network_session.h" | |
28 #include "net/http/http_network_session_peer.h" | |
29 #include "net/http/http_network_transaction.h" | |
30 #include "net/http/http_server_properties.h" | |
31 #include "net/http/http_transaction_test_util.h" | |
32 #include "net/log/net_log_event_type.h" | |
33 #include "net/log/net_log_with_source.h" | |
34 #include "net/log/test_net_log.h" | |
35 #include "net/log/test_net_log_entry.h" | |
36 #include "net/log/test_net_log_util.h" | |
37 #include "net/proxy/proxy_server.h" | |
38 #include "net/socket/client_socket_pool_base.h" | |
39 #include "net/socket/next_proto.h" | |
40 #include "net/spdy/buffered_spdy_framer.h" | |
41 #include "net/spdy/platform/api/spdy_string.h" | |
42 #include "net/spdy/platform/api/spdy_string_piece.h" | |
43 #include "net/spdy/spdy_http_stream.h" | |
44 #include "net/spdy/spdy_http_utils.h" | |
45 #include "net/spdy/spdy_protocol.h" | |
46 #include "net/spdy/spdy_session.h" | |
47 #include "net/spdy/spdy_session_pool.h" | |
48 #include "net/spdy/spdy_test_util_common.h" | |
49 #include "net/spdy/spdy_test_utils.h" | |
50 #include "net/ssl/ssl_connection_status_flags.h" | |
51 #include "net/test/cert_test_util.h" | |
52 #include "net/test/gtest_util.h" | |
53 #include "net/test/test_data_directory.h" | |
54 #include "net/url_request/url_request_test_util.h" | |
55 #include "testing/gmock/include/gmock/gmock.h" | |
56 #include "testing/platform_test.h" | |
57 | |
58 using net::test::IsError; | |
59 using net::test::IsOk; | |
60 | |
61 //----------------------------------------------------------------------------- | |
62 | |
63 namespace net { | |
64 | |
65 namespace { | |
66 | |
67 using testing::Each; | |
68 using testing::Eq; | |
69 | |
70 const int32_t kBufferSize = SpdyHttpStream::kRequestBodyBufferSize; | |
71 | |
72 } // namespace | |
73 | |
74 class SpdyNetworkTransactionTest : public ::testing::Test { | |
75 protected: | |
76 SpdyNetworkTransactionTest() | |
77 : default_url_(kDefaultUrl), | |
78 host_port_pair_(HostPortPair::FromURL(default_url_)) {} | |
79 | |
80 ~SpdyNetworkTransactionTest() override { | |
81 // UploadDataStream may post a deletion tasks back to the message loop on | |
82 // destruction. | |
83 upload_data_stream_.reset(); | |
84 base::RunLoop().RunUntilIdle(); | |
85 } | |
86 | |
87 void SetUp() override { | |
88 get_request_initialized_ = false; | |
89 post_request_initialized_ = false; | |
90 chunked_post_request_initialized_ = false; | |
91 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | |
92 } | |
93 | |
94 struct TransactionHelperResult { | |
95 int rv; | |
96 SpdyString status_line; | |
97 SpdyString response_data; | |
98 HttpResponseInfo response_info; | |
99 }; | |
100 | |
101 // A helper class that handles all the initial npn/ssl setup. | |
102 class NormalSpdyTransactionHelper { | |
103 public: | |
104 NormalSpdyTransactionHelper( | |
105 const HttpRequestInfo& request, | |
106 RequestPriority priority, | |
107 const NetLogWithSource& log, | |
108 std::unique_ptr<SpdySessionDependencies> session_deps) | |
109 : request_(request), | |
110 priority_(priority), | |
111 session_deps_(session_deps.get() == nullptr | |
112 ? base::MakeUnique<SpdySessionDependencies>() | |
113 : std::move(session_deps)), | |
114 log_(log) { | |
115 session_deps_->net_log = log.net_log(); | |
116 session_ = | |
117 SpdySessionDependencies::SpdyCreateSession(session_deps_.get()); | |
118 } | |
119 | |
120 ~NormalSpdyTransactionHelper() { | |
121 // Any test which doesn't close the socket by sending it an EOF will | |
122 // have a valid session left open, which leaks the entire session pool. | |
123 // This is just fine - in fact, some of our tests intentionally do this | |
124 // so that we can check consistency of the SpdySessionPool as the test | |
125 // finishes. If we had put an EOF on the socket, the SpdySession would | |
126 // have closed and we wouldn't be able to check the consistency. | |
127 | |
128 // Forcefully close existing sessions here. | |
129 session()->spdy_session_pool()->CloseAllSessions(); | |
130 } | |
131 | |
132 void RunPreTestSetup() { | |
133 // We're now ready to use SSL-npn SPDY. | |
134 trans_.reset(new HttpNetworkTransaction(priority_, session_.get())); | |
135 } | |
136 | |
137 // Start the transaction, read some data, finish. | |
138 void RunDefaultTest() { | |
139 if (!StartDefaultTest()) | |
140 return; | |
141 FinishDefaultTest(); | |
142 } | |
143 | |
144 bool StartDefaultTest() { | |
145 output_.rv = trans_->Start(&request_, callback_.callback(), log_); | |
146 | |
147 // We expect an IO Pending or some sort of error. | |
148 EXPECT_LT(output_.rv, 0); | |
149 return output_.rv == ERR_IO_PENDING; | |
150 } | |
151 | |
152 void FinishDefaultTest() { | |
153 output_.rv = callback_.WaitForResult(); | |
154 // Finish async network reads/writes. | |
155 base::RunLoop().RunUntilIdle(); | |
156 if (output_.rv != OK) { | |
157 session_->spdy_session_pool()->CloseCurrentSessions(ERR_ABORTED); | |
158 return; | |
159 } | |
160 | |
161 // Verify responses. | |
162 const HttpResponseInfo* response = trans_->GetResponseInfo(); | |
163 ASSERT_TRUE(response); | |
164 ASSERT_TRUE(response->headers); | |
165 EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2, | |
166 response->connection_info); | |
167 EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); | |
168 EXPECT_TRUE(response->was_fetched_via_spdy); | |
169 EXPECT_TRUE(response->was_alpn_negotiated); | |
170 EXPECT_EQ("127.0.0.1", response->socket_address.host()); | |
171 EXPECT_EQ(443, response->socket_address.port()); | |
172 output_.status_line = response->headers->GetStatusLine(); | |
173 output_.response_info = *response; // Make a copy so we can verify. | |
174 output_.rv = ReadTransaction(trans_.get(), &output_.response_data); | |
175 } | |
176 | |
177 void FinishDefaultTestWithoutVerification() { | |
178 output_.rv = callback_.WaitForResult(); | |
179 // Finish async network reads/writes. | |
180 base::RunLoop().RunUntilIdle(); | |
181 if (output_.rv != OK) | |
182 session_->spdy_session_pool()->CloseCurrentSessions(ERR_ABORTED); | |
183 } | |
184 | |
185 void WaitForCallbackToComplete() { output_.rv = callback_.WaitForResult(); } | |
186 | |
187 // Most tests will want to call this function. In particular, the MockReads | |
188 // should end with an empty read, and that read needs to be processed to | |
189 // ensure proper deletion of the spdy_session_pool. | |
190 void VerifyDataConsumed() { | |
191 for (const SocketDataProvider* provider : data_vector_) { | |
192 EXPECT_TRUE(provider->AllReadDataConsumed()); | |
193 EXPECT_TRUE(provider->AllWriteDataConsumed()); | |
194 } | |
195 } | |
196 | |
197 // Occasionally a test will expect to error out before certain reads are | |
198 // processed. In that case we want to explicitly ensure that the reads were | |
199 // not processed. | |
200 void VerifyDataNotConsumed() { | |
201 for (const SocketDataProvider* provider : data_vector_) { | |
202 EXPECT_FALSE(provider->AllReadDataConsumed()); | |
203 EXPECT_FALSE(provider->AllWriteDataConsumed()); | |
204 } | |
205 } | |
206 | |
207 void RunToCompletion(SocketDataProvider* data) { | |
208 RunPreTestSetup(); | |
209 AddData(data); | |
210 RunDefaultTest(); | |
211 VerifyDataConsumed(); | |
212 } | |
213 | |
214 void RunToCompletionWithSSLData( | |
215 SocketDataProvider* data, | |
216 std::unique_ptr<SSLSocketDataProvider> ssl_provider) { | |
217 RunPreTestSetup(); | |
218 AddDataWithSSLSocketDataProvider(data, std::move(ssl_provider)); | |
219 RunDefaultTest(); | |
220 VerifyDataConsumed(); | |
221 } | |
222 | |
223 void AddData(SocketDataProvider* data) { | |
224 std::unique_ptr<SSLSocketDataProvider> ssl_provider( | |
225 new SSLSocketDataProvider(ASYNC, OK)); | |
226 ssl_provider->cert = | |
227 ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); | |
228 AddDataWithSSLSocketDataProvider(data, std::move(ssl_provider)); | |
229 } | |
230 | |
231 void AddDataWithSSLSocketDataProvider( | |
232 SocketDataProvider* data, | |
233 std::unique_ptr<SSLSocketDataProvider> ssl_provider) { | |
234 data_vector_.push_back(data); | |
235 if (ssl_provider->next_proto == kProtoUnknown) | |
236 ssl_provider->next_proto = kProtoHTTP2; | |
237 | |
238 session_deps_->socket_factory->AddSSLSocketDataProvider( | |
239 ssl_provider.get()); | |
240 ssl_vector_.push_back(std::move(ssl_provider)); | |
241 | |
242 session_deps_->socket_factory->AddSocketDataProvider(data); | |
243 } | |
244 | |
245 HttpNetworkTransaction* trans() { return trans_.get(); } | |
246 void ResetTrans() { trans_.reset(); } | |
247 const TransactionHelperResult& output() { return output_; } | |
248 const HttpRequestInfo& request() const { return request_; } | |
249 HttpNetworkSession* session() const { return session_.get(); } | |
250 SpdySessionDependencies* session_deps() { return session_deps_.get(); } | |
251 | |
252 private: | |
253 typedef std::vector<SocketDataProvider*> DataVector; | |
254 typedef std::vector<std::unique_ptr<SSLSocketDataProvider>> SSLVector; | |
255 typedef std::vector<std::unique_ptr<SocketDataProvider>> AlternateVector; | |
256 HttpRequestInfo request_; | |
257 RequestPriority priority_; | |
258 std::unique_ptr<SpdySessionDependencies> session_deps_; | |
259 std::unique_ptr<HttpNetworkSession> session_; | |
260 TransactionHelperResult output_; | |
261 SSLVector ssl_vector_; | |
262 TestCompletionCallback callback_; | |
263 std::unique_ptr<HttpNetworkTransaction> trans_; | |
264 DataVector data_vector_; | |
265 const NetLogWithSource log_; | |
266 }; | |
267 | |
268 void ConnectStatusHelperWithExpectedStatus(const MockRead& status, | |
269 int expected_status); | |
270 | |
271 void ConnectStatusHelper(const MockRead& status); | |
272 | |
273 const HttpRequestInfo& CreateGetPushRequest() { | |
274 get_push_request_.method = "GET"; | |
275 get_push_request_.url = GURL(GetDefaultUrlWithPath("/foo.dat")); | |
276 get_push_request_.load_flags = 0; | |
277 return get_push_request_; | |
278 } | |
279 | |
280 const HttpRequestInfo& CreateGetRequest() { | |
281 if (!get_request_initialized_) { | |
282 get_request_.method = "GET"; | |
283 get_request_.url = default_url_; | |
284 get_request_.load_flags = 0; | |
285 get_request_initialized_ = true; | |
286 } | |
287 return get_request_; | |
288 } | |
289 | |
290 const HttpRequestInfo& CreateGetRequestWithUserAgent() { | |
291 if (!get_request_initialized_) { | |
292 get_request_.method = "GET"; | |
293 get_request_.url = default_url_; | |
294 get_request_.load_flags = 0; | |
295 get_request_.extra_headers.SetHeader("User-Agent", "Chrome"); | |
296 get_request_initialized_ = true; | |
297 } | |
298 return get_request_; | |
299 } | |
300 | |
301 const HttpRequestInfo& CreatePostRequest() { | |
302 if (!post_request_initialized_) { | |
303 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
304 element_readers.push_back(base::MakeUnique<UploadBytesElementReader>( | |
305 kUploadData, kUploadDataSize)); | |
306 upload_data_stream_.reset( | |
307 new ElementsUploadDataStream(std::move(element_readers), 0)); | |
308 | |
309 post_request_.method = "POST"; | |
310 post_request_.url = default_url_; | |
311 post_request_.upload_data_stream = upload_data_stream_.get(); | |
312 post_request_initialized_ = true; | |
313 } | |
314 return post_request_; | |
315 } | |
316 | |
317 const HttpRequestInfo& CreateFilePostRequest() { | |
318 if (!post_request_initialized_) { | |
319 base::FilePath file_path; | |
320 CHECK(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &file_path)); | |
321 CHECK_EQ(static_cast<int>(kUploadDataSize), | |
322 base::WriteFile(file_path, kUploadData, kUploadDataSize)); | |
323 | |
324 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
325 element_readers.push_back(base::MakeUnique<UploadFileElementReader>( | |
326 base::ThreadTaskRunnerHandle::Get().get(), file_path, 0, | |
327 kUploadDataSize, base::Time())); | |
328 upload_data_stream_.reset( | |
329 new ElementsUploadDataStream(std::move(element_readers), 0)); | |
330 | |
331 post_request_.method = "POST"; | |
332 post_request_.url = default_url_; | |
333 post_request_.upload_data_stream = upload_data_stream_.get(); | |
334 post_request_initialized_ = true; | |
335 } | |
336 return post_request_; | |
337 } | |
338 | |
339 const HttpRequestInfo& CreateUnreadableFilePostRequest() { | |
340 if (post_request_initialized_) | |
341 return post_request_; | |
342 | |
343 base::FilePath file_path; | |
344 CHECK(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &file_path)); | |
345 CHECK_EQ(static_cast<int>(kUploadDataSize), | |
346 base::WriteFile(file_path, kUploadData, kUploadDataSize)); | |
347 CHECK(base::MakeFileUnreadable(file_path)); | |
348 | |
349 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
350 element_readers.push_back(base::MakeUnique<UploadFileElementReader>( | |
351 base::ThreadTaskRunnerHandle::Get().get(), file_path, 0, | |
352 kUploadDataSize, base::Time())); | |
353 upload_data_stream_.reset( | |
354 new ElementsUploadDataStream(std::move(element_readers), 0)); | |
355 | |
356 post_request_.method = "POST"; | |
357 post_request_.url = default_url_; | |
358 post_request_.upload_data_stream = upload_data_stream_.get(); | |
359 post_request_initialized_ = true; | |
360 return post_request_; | |
361 } | |
362 | |
363 const HttpRequestInfo& CreateComplexPostRequest() { | |
364 if (!post_request_initialized_) { | |
365 const int kFileRangeOffset = 1; | |
366 const int kFileRangeLength = 3; | |
367 CHECK_LT(kFileRangeOffset + kFileRangeLength, kUploadDataSize); | |
368 | |
369 base::FilePath file_path; | |
370 CHECK(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &file_path)); | |
371 CHECK_EQ(static_cast<int>(kUploadDataSize), | |
372 base::WriteFile(file_path, kUploadData, kUploadDataSize)); | |
373 | |
374 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
375 element_readers.push_back(base::MakeUnique<UploadBytesElementReader>( | |
376 kUploadData, kFileRangeOffset)); | |
377 element_readers.push_back(base::MakeUnique<UploadFileElementReader>( | |
378 base::ThreadTaskRunnerHandle::Get().get(), file_path, | |
379 kFileRangeOffset, kFileRangeLength, base::Time())); | |
380 element_readers.push_back(base::MakeUnique<UploadBytesElementReader>( | |
381 kUploadData + kFileRangeOffset + kFileRangeLength, | |
382 kUploadDataSize - (kFileRangeOffset + kFileRangeLength))); | |
383 upload_data_stream_.reset( | |
384 new ElementsUploadDataStream(std::move(element_readers), 0)); | |
385 | |
386 post_request_.method = "POST"; | |
387 post_request_.url = default_url_; | |
388 post_request_.upload_data_stream = upload_data_stream_.get(); | |
389 post_request_initialized_ = true; | |
390 } | |
391 return post_request_; | |
392 } | |
393 | |
394 const HttpRequestInfo& CreateChunkedPostRequest() { | |
395 if (!chunked_post_request_initialized_) { | |
396 upload_chunked_data_stream_.reset(new ChunkedUploadDataStream(0)); | |
397 chunked_post_request_.method = "POST"; | |
398 chunked_post_request_.url = default_url_; | |
399 chunked_post_request_.upload_data_stream = | |
400 upload_chunked_data_stream_.get(); | |
401 chunked_post_request_initialized_ = true; | |
402 } | |
403 return chunked_post_request_; | |
404 } | |
405 | |
406 // Read the result of a particular transaction, knowing that we've got | |
407 // multiple transactions in the read pipeline; so as we read, we may have | |
408 // to skip over data destined for other transactions while we consume | |
409 // the data for |trans|. | |
410 int ReadResult(HttpNetworkTransaction* trans, SpdyString* result) { | |
411 const int kSize = 3000; | |
412 | |
413 int bytes_read = 0; | |
414 scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(kSize)); | |
415 TestCompletionCallback callback; | |
416 while (true) { | |
417 int rv = trans->Read(buf.get(), kSize, callback.callback()); | |
418 if (rv == ERR_IO_PENDING) { | |
419 rv = callback.WaitForResult(); | |
420 } else if (rv <= 0) { | |
421 break; | |
422 } | |
423 result->append(buf->data(), rv); | |
424 bytes_read += rv; | |
425 } | |
426 return bytes_read; | |
427 } | |
428 | |
429 void VerifyStreamsClosed(const NormalSpdyTransactionHelper& helper) { | |
430 // This lengthy block is reaching into the pool to dig out the active | |
431 // session. Once we have the session, we verify that the streams are | |
432 // all closed and not leaked at this point. | |
433 const GURL& url = helper.request().url; | |
434 SpdySessionKey key(HostPortPair::FromURL(url), ProxyServer::Direct(), | |
435 PRIVACY_MODE_DISABLED); | |
436 NetLogWithSource log; | |
437 HttpNetworkSession* session = helper.session(); | |
438 base::WeakPtr<SpdySession> spdy_session = | |
439 session->spdy_session_pool()->FindAvailableSession( | |
440 key, url, | |
441 /* enable_ip_based_pooling = */ true, log); | |
442 ASSERT_TRUE(spdy_session); | |
443 EXPECT_EQ(0u, spdy_session->num_active_streams()); | |
444 EXPECT_EQ(0u, spdy_session->num_unclaimed_pushed_streams()); | |
445 } | |
446 | |
447 void RunServerPushTest(SequencedSocketData* data, | |
448 HttpResponseInfo* response, | |
449 HttpResponseInfo* push_response, | |
450 const SpdyString& expected) { | |
451 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
452 NetLogWithSource(), nullptr); | |
453 helper.RunPreTestSetup(); | |
454 helper.AddData(data); | |
455 | |
456 HttpNetworkTransaction* trans = helper.trans(); | |
457 | |
458 // Start the transaction with basic parameters. | |
459 TestCompletionCallback callback; | |
460 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
461 NetLogWithSource()); | |
462 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
463 rv = callback.WaitForResult(); | |
464 | |
465 // Finish async network reads/writes. | |
466 base::RunLoop().RunUntilIdle(); | |
467 | |
468 // Request the pushed path. | |
469 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
470 rv = trans2.Start(&CreateGetPushRequest(), callback.callback(), | |
471 NetLogWithSource()); | |
472 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
473 base::RunLoop().RunUntilIdle(); | |
474 | |
475 // The data for the pushed path may be coming in more than 1 frame. Compile | |
476 // the results into a single string. | |
477 | |
478 // Read the server push body. | |
479 SpdyString result2; | |
480 ReadResult(&trans2, &result2); | |
481 // Read the response body. | |
482 SpdyString result; | |
483 ReadResult(trans, &result); | |
484 | |
485 // Verify that we consumed all test data. | |
486 EXPECT_TRUE(data->AllReadDataConsumed()); | |
487 EXPECT_TRUE(data->AllWriteDataConsumed()); | |
488 | |
489 LoadTimingInfo load_timing_info; | |
490 EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); | |
491 EXPECT_TRUE(load_timing_info.push_start.is_null()); | |
492 EXPECT_TRUE(load_timing_info.push_end.is_null()); | |
493 | |
494 LoadTimingInfo load_timing_info2; | |
495 EXPECT_TRUE(trans2.GetLoadTimingInfo(&load_timing_info2)); | |
496 EXPECT_FALSE(load_timing_info2.push_start.is_null()); | |
497 EXPECT_FALSE(load_timing_info2.push_end.is_null()); | |
498 | |
499 // Verify that the received push data is same as the expected push data. | |
500 EXPECT_EQ(result2.compare(expected), 0) << "Received data: " | |
501 << result2 | |
502 << "||||| Expected data: " | |
503 << expected; | |
504 | |
505 // Verify the response HEADERS. | |
506 // Copy the response info, because trans goes away. | |
507 *response = *trans->GetResponseInfo(); | |
508 *push_response = *trans2.GetResponseInfo(); | |
509 | |
510 VerifyStreamsClosed(helper); | |
511 } | |
512 | |
513 void RunBrokenPushTest(SequencedSocketData* data, int expected_rv) { | |
514 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
515 NetLogWithSource(), nullptr); | |
516 helper.RunPreTestSetup(); | |
517 helper.AddData(data); | |
518 | |
519 HttpNetworkTransaction* trans = helper.trans(); | |
520 | |
521 // Start the transaction with basic parameters. | |
522 TestCompletionCallback callback; | |
523 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
524 NetLogWithSource()); | |
525 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
526 rv = callback.WaitForResult(); | |
527 EXPECT_EQ(expected_rv, rv); | |
528 | |
529 // Finish async network reads/writes. | |
530 base::RunLoop().RunUntilIdle(); | |
531 | |
532 // Verify that we consumed all test data. | |
533 EXPECT_TRUE(data->AllReadDataConsumed()); | |
534 EXPECT_TRUE(data->AllWriteDataConsumed()); | |
535 | |
536 if (expected_rv == OK) { | |
537 // Expected main request to succeed, even if push failed. | |
538 HttpResponseInfo response = *trans->GetResponseInfo(); | |
539 EXPECT_TRUE(response.headers); | |
540 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
541 } | |
542 } | |
543 | |
544 static void DeleteSessionCallback(NormalSpdyTransactionHelper* helper, | |
545 int result) { | |
546 helper->ResetTrans(); | |
547 } | |
548 | |
549 static void StartTransactionCallback(HttpNetworkSession* session, | |
550 GURL url, | |
551 int result) { | |
552 HttpRequestInfo request; | |
553 HttpNetworkTransaction trans(DEFAULT_PRIORITY, session); | |
554 TestCompletionCallback callback; | |
555 request.method = "GET"; | |
556 request.url = url; | |
557 request.load_flags = 0; | |
558 int rv = trans.Start(&request, callback.callback(), NetLogWithSource()); | |
559 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
560 callback.WaitForResult(); | |
561 } | |
562 | |
563 ChunkedUploadDataStream* upload_chunked_data_stream() const { | |
564 return upload_chunked_data_stream_.get(); | |
565 } | |
566 | |
567 SpdyString GetDefaultUrlWithPath(const char* path) { | |
568 return SpdyString(kDefaultUrl) + path; | |
569 } | |
570 | |
571 const GURL default_url_; | |
572 const HostPortPair host_port_pair_; | |
573 SpdyTestUtil spdy_util_; | |
574 | |
575 private: | |
576 std::unique_ptr<ChunkedUploadDataStream> upload_chunked_data_stream_; | |
577 std::unique_ptr<UploadDataStream> upload_data_stream_; | |
578 bool get_request_initialized_; | |
579 bool post_request_initialized_; | |
580 bool chunked_post_request_initialized_; | |
581 HttpRequestInfo get_request_; | |
582 HttpRequestInfo post_request_; | |
583 HttpRequestInfo chunked_post_request_; | |
584 HttpRequestInfo get_push_request_; | |
585 base::ScopedTempDir temp_dir_; | |
586 }; | |
587 | |
588 // Verify HttpNetworkTransaction constructor. | |
589 TEST_F(SpdyNetworkTransactionTest, Constructor) { | |
590 auto session_deps = base::MakeUnique<SpdySessionDependencies>(); | |
591 std::unique_ptr<HttpNetworkSession> session( | |
592 SpdySessionDependencies::SpdyCreateSession(session_deps.get())); | |
593 auto trans = | |
594 base::MakeUnique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get()); | |
595 } | |
596 | |
597 TEST_F(SpdyNetworkTransactionTest, Get) { | |
598 // Construct the request. | |
599 SpdySerializedFrame req( | |
600 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
601 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
602 | |
603 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
604 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
605 MockRead reads[] = { | |
606 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
607 MockRead(ASYNC, 0, 3) // EOF | |
608 }; | |
609 | |
610 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
611 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
612 NetLogWithSource(), nullptr); | |
613 helper.RunToCompletion(&data); | |
614 TransactionHelperResult out = helper.output(); | |
615 EXPECT_THAT(out.rv, IsOk()); | |
616 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
617 EXPECT_EQ("hello!", out.response_data); | |
618 } | |
619 | |
620 TEST_F(SpdyNetworkTransactionTest, GetAtEachPriority) { | |
621 for (RequestPriority p = MINIMUM_PRIORITY; p <= MAXIMUM_PRIORITY; | |
622 p = RequestPriority(p + 1)) { | |
623 SpdyTestUtil spdy_test_util; | |
624 | |
625 // Construct the request. | |
626 SpdySerializedFrame req( | |
627 spdy_test_util.ConstructSpdyGet(nullptr, 0, 1, p, true)); | |
628 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
629 | |
630 SpdyPriority spdy_prio = 0; | |
631 EXPECT_TRUE(GetSpdyPriority(req, &spdy_prio)); | |
632 // this repeats the RequestPriority-->SpdyPriority mapping from | |
633 // SpdyFramer::ConvertRequestPriorityToSpdyPriority to make | |
634 // sure it's being done right. | |
635 switch (p) { | |
636 case HIGHEST: | |
637 EXPECT_EQ(0, spdy_prio); | |
638 break; | |
639 case MEDIUM: | |
640 EXPECT_EQ(1, spdy_prio); | |
641 break; | |
642 case LOW: | |
643 EXPECT_EQ(2, spdy_prio); | |
644 break; | |
645 case LOWEST: | |
646 EXPECT_EQ(3, spdy_prio); | |
647 break; | |
648 case IDLE: | |
649 EXPECT_EQ(4, spdy_prio); | |
650 break; | |
651 case THROTTLED: | |
652 EXPECT_EQ(5, spdy_prio); | |
653 break; | |
654 default: | |
655 FAIL(); | |
656 } | |
657 | |
658 SpdySerializedFrame resp( | |
659 spdy_test_util.ConstructSpdyGetReply(nullptr, 0, 1)); | |
660 SpdySerializedFrame body(spdy_test_util.ConstructSpdyDataFrame(1, true)); | |
661 MockRead reads[] = { | |
662 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
663 MockRead(ASYNC, 0, 3) // EOF | |
664 }; | |
665 | |
666 SequencedSocketData data(reads, arraysize(reads), writes, | |
667 arraysize(writes)); | |
668 HttpRequestInfo http_req = CreateGetRequest(); | |
669 | |
670 NormalSpdyTransactionHelper helper(http_req, p, NetLogWithSource(), | |
671 nullptr); | |
672 helper.RunToCompletion(&data); | |
673 TransactionHelperResult out = helper.output(); | |
674 EXPECT_THAT(out.rv, IsOk()); | |
675 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
676 EXPECT_EQ("hello!", out.response_data); | |
677 } | |
678 } | |
679 | |
680 // Start three gets simultaniously; making sure that multiplexed | |
681 // streams work properly. | |
682 | |
683 // This can't use the TransactionHelper method, since it only | |
684 // handles a single transaction, and finishes them as soon | |
685 // as it launches them. | |
686 | |
687 // TODO(gavinp): create a working generalized TransactionHelper that | |
688 // can allow multiple streams in flight. | |
689 | |
690 TEST_F(SpdyNetworkTransactionTest, ThreeGets) { | |
691 SpdySerializedFrame req( | |
692 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
693 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
694 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
695 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
696 | |
697 SpdySerializedFrame req2( | |
698 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
699 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
700 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
701 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
702 | |
703 SpdySerializedFrame req3( | |
704 spdy_util_.ConstructSpdyGet(nullptr, 0, 5, LOWEST, true)); | |
705 SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5)); | |
706 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, false)); | |
707 SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(5, true)); | |
708 | |
709 MockWrite writes[] = { | |
710 CreateMockWrite(req, 0), CreateMockWrite(req2, 3), | |
711 CreateMockWrite(req3, 6), | |
712 }; | |
713 MockRead reads[] = { | |
714 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
715 CreateMockRead(resp2, 4), CreateMockRead(body2, 5), | |
716 CreateMockRead(resp3, 7), CreateMockRead(body3, 8), | |
717 | |
718 CreateMockRead(fbody, 9), CreateMockRead(fbody2, 10), | |
719 CreateMockRead(fbody3, 11), | |
720 | |
721 MockRead(ASYNC, 0, 12), // EOF | |
722 }; | |
723 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
724 SequencedSocketData data_placeholder1(nullptr, 0, nullptr, 0); | |
725 SequencedSocketData data_placeholder2(nullptr, 0, nullptr, 0); | |
726 | |
727 NetLogWithSource log; | |
728 TransactionHelperResult out; | |
729 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
730 NetLogWithSource(), nullptr); | |
731 helper.RunPreTestSetup(); | |
732 helper.AddData(&data); | |
733 // We require placeholder data because three get requests are sent out at | |
734 // the same time which results in three sockets being connected. The first | |
735 // on will negotiate SPDY and will be used for all requests. | |
736 helper.AddData(&data_placeholder1); | |
737 helper.AddData(&data_placeholder2); | |
738 TestCompletionCallback callback1; | |
739 TestCompletionCallback callback2; | |
740 TestCompletionCallback callback3; | |
741 | |
742 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
743 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
744 HttpRequestInfo httpreq3 = CreateGetRequest(); | |
745 | |
746 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
747 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
748 HttpNetworkTransaction trans3(DEFAULT_PRIORITY, helper.session()); | |
749 | |
750 out.rv = trans1.Start(&httpreq1, callback1.callback(), log); | |
751 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
752 out.rv = trans2.Start(&httpreq2, callback2.callback(), log); | |
753 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
754 out.rv = trans3.Start(&httpreq3, callback3.callback(), log); | |
755 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
756 | |
757 out.rv = callback1.WaitForResult(); | |
758 ASSERT_THAT(out.rv, IsOk()); | |
759 out.rv = callback3.WaitForResult(); | |
760 ASSERT_THAT(out.rv, IsOk()); | |
761 | |
762 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
763 EXPECT_TRUE(response1->headers); | |
764 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
765 out.status_line = response1->headers->GetStatusLine(); | |
766 out.response_info = *response1; | |
767 | |
768 trans2.GetResponseInfo(); | |
769 | |
770 out.rv = ReadTransaction(&trans1, &out.response_data); | |
771 helper.VerifyDataConsumed(); | |
772 EXPECT_THAT(out.rv, IsOk()); | |
773 | |
774 EXPECT_THAT(out.rv, IsOk()); | |
775 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
776 EXPECT_EQ("hello!hello!", out.response_data); | |
777 } | |
778 | |
779 TEST_F(SpdyNetworkTransactionTest, TwoGetsLateBinding) { | |
780 SpdySerializedFrame req( | |
781 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
782 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
783 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
784 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
785 | |
786 SpdySerializedFrame req2( | |
787 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
788 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
789 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
790 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
791 | |
792 MockWrite writes[] = { | |
793 CreateMockWrite(req, 0), CreateMockWrite(req2, 3), | |
794 }; | |
795 MockRead reads[] = { | |
796 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
797 CreateMockRead(resp2, 4), CreateMockRead(body2, 5), | |
798 CreateMockRead(fbody, 6), CreateMockRead(fbody2, 7), | |
799 MockRead(ASYNC, 0, 8), // EOF | |
800 }; | |
801 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
802 | |
803 MockConnect never_finishing_connect(SYNCHRONOUS, ERR_IO_PENDING); | |
804 SequencedSocketData data_placeholder(nullptr, 0, nullptr, 0); | |
805 data_placeholder.set_connect_data(never_finishing_connect); | |
806 | |
807 NetLogWithSource log; | |
808 TransactionHelperResult out; | |
809 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
810 NetLogWithSource(), nullptr); | |
811 helper.RunPreTestSetup(); | |
812 helper.AddData(&data); | |
813 // We require placeholder data because two requests are sent out at | |
814 // the same time which results in two sockets being connected. The first | |
815 // on will negotiate SPDY and will be used for all requests. | |
816 helper.AddData(&data_placeholder); | |
817 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
818 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
819 | |
820 TestCompletionCallback callback1; | |
821 TestCompletionCallback callback2; | |
822 | |
823 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
824 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
825 | |
826 out.rv = trans1.Start(&httpreq1, callback1.callback(), log); | |
827 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
828 out.rv = trans2.Start(&httpreq2, callback2.callback(), log); | |
829 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
830 | |
831 out.rv = callback1.WaitForResult(); | |
832 ASSERT_THAT(out.rv, IsOk()); | |
833 out.rv = callback2.WaitForResult(); | |
834 ASSERT_THAT(out.rv, IsOk()); | |
835 | |
836 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
837 EXPECT_TRUE(response1->headers); | |
838 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
839 out.status_line = response1->headers->GetStatusLine(); | |
840 out.response_info = *response1; | |
841 out.rv = ReadTransaction(&trans1, &out.response_data); | |
842 EXPECT_THAT(out.rv, IsOk()); | |
843 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
844 EXPECT_EQ("hello!hello!", out.response_data); | |
845 | |
846 const HttpResponseInfo* response2 = trans2.GetResponseInfo(); | |
847 EXPECT_TRUE(response2->headers); | |
848 EXPECT_TRUE(response2->was_fetched_via_spdy); | |
849 out.status_line = response2->headers->GetStatusLine(); | |
850 out.response_info = *response2; | |
851 out.rv = ReadTransaction(&trans2, &out.response_data); | |
852 EXPECT_THAT(out.rv, IsOk()); | |
853 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
854 EXPECT_EQ("hello!hello!", out.response_data); | |
855 | |
856 helper.VerifyDataConsumed(); | |
857 } | |
858 | |
859 TEST_F(SpdyNetworkTransactionTest, TwoGetsLateBindingFromPreconnect) { | |
860 SpdySerializedFrame req( | |
861 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
862 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
863 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
864 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
865 | |
866 SpdySerializedFrame req2( | |
867 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
868 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
869 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
870 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
871 | |
872 MockWrite writes[] = { | |
873 CreateMockWrite(req, 0), CreateMockWrite(req2, 3), | |
874 }; | |
875 MockRead reads[] = { | |
876 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
877 CreateMockRead(resp2, 4), CreateMockRead(body2, 5), | |
878 CreateMockRead(fbody, 6), CreateMockRead(fbody2, 7), | |
879 MockRead(ASYNC, 0, 8), // EOF | |
880 }; | |
881 SequencedSocketData preconnect_data(reads, arraysize(reads), writes, | |
882 arraysize(writes)); | |
883 | |
884 MockConnect never_finishing_connect(ASYNC, ERR_IO_PENDING); | |
885 | |
886 SequencedSocketData data_placeholder(nullptr, 0, nullptr, 0); | |
887 data_placeholder.set_connect_data(never_finishing_connect); | |
888 | |
889 NetLogWithSource log; | |
890 TransactionHelperResult out; | |
891 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
892 NetLogWithSource(), nullptr); | |
893 helper.RunPreTestSetup(); | |
894 helper.AddData(&preconnect_data); | |
895 // We require placeholder data because 3 connections are attempted (first is | |
896 // the preconnect, 2nd and 3rd are the never finished connections. | |
897 helper.AddData(&data_placeholder); | |
898 helper.AddData(&data_placeholder); | |
899 | |
900 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
901 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
902 | |
903 TestCompletionCallback callback1; | |
904 TestCompletionCallback callback2; | |
905 | |
906 HttpRequestInfo httpreq = CreateGetRequest(); | |
907 | |
908 // Preconnect the first. | |
909 HttpStreamFactory* http_stream_factory = | |
910 helper.session()->http_stream_factory(); | |
911 | |
912 http_stream_factory->PreconnectStreams(1, httpreq); | |
913 | |
914 out.rv = trans1.Start(&httpreq, callback1.callback(), log); | |
915 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
916 out.rv = trans2.Start(&httpreq, callback2.callback(), log); | |
917 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
918 | |
919 out.rv = callback1.WaitForResult(); | |
920 ASSERT_THAT(out.rv, IsOk()); | |
921 out.rv = callback2.WaitForResult(); | |
922 ASSERT_THAT(out.rv, IsOk()); | |
923 | |
924 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
925 EXPECT_TRUE(response1->headers); | |
926 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
927 out.status_line = response1->headers->GetStatusLine(); | |
928 out.response_info = *response1; | |
929 out.rv = ReadTransaction(&trans1, &out.response_data); | |
930 EXPECT_THAT(out.rv, IsOk()); | |
931 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
932 EXPECT_EQ("hello!hello!", out.response_data); | |
933 | |
934 const HttpResponseInfo* response2 = trans2.GetResponseInfo(); | |
935 EXPECT_TRUE(response2->headers); | |
936 EXPECT_TRUE(response2->was_fetched_via_spdy); | |
937 out.status_line = response2->headers->GetStatusLine(); | |
938 out.response_info = *response2; | |
939 out.rv = ReadTransaction(&trans2, &out.response_data); | |
940 EXPECT_THAT(out.rv, IsOk()); | |
941 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
942 EXPECT_EQ("hello!hello!", out.response_data); | |
943 | |
944 helper.VerifyDataConsumed(); | |
945 } | |
946 | |
947 // Similar to ThreeGets above, however this test adds a SETTINGS | |
948 // frame. The SETTINGS frame is read during the IO loop waiting on | |
949 // the first transaction completion, and sets a maximum concurrent | |
950 // stream limit of 1. This means that our IO loop exists after the | |
951 // second transaction completes, so we can assert on read_index(). | |
952 TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrent) { | |
953 // Construct the request. | |
954 // Each request fully completes before the next starts. | |
955 SpdySerializedFrame req( | |
956 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
957 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
958 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
959 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
960 spdy_util_.UpdateWithStreamDestruction(1); | |
961 | |
962 SpdySerializedFrame req2( | |
963 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
964 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
965 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
966 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
967 spdy_util_.UpdateWithStreamDestruction(3); | |
968 | |
969 SpdySerializedFrame req3( | |
970 spdy_util_.ConstructSpdyGet(nullptr, 0, 5, LOWEST, true)); | |
971 SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5)); | |
972 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, false)); | |
973 SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(5, true)); | |
974 | |
975 SettingsMap settings; | |
976 const uint32_t max_concurrent_streams = 1; | |
977 settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; | |
978 SpdySerializedFrame settings_frame( | |
979 spdy_util_.ConstructSpdySettings(settings)); | |
980 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
981 | |
982 MockWrite writes[] = { | |
983 CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5), | |
984 CreateMockWrite(req2, 6), CreateMockWrite(req3, 10), | |
985 }; | |
986 | |
987 MockRead reads[] = { | |
988 CreateMockRead(settings_frame, 1), | |
989 CreateMockRead(resp, 2), | |
990 CreateMockRead(body, 3), | |
991 CreateMockRead(fbody, 4), | |
992 CreateMockRead(resp2, 7), | |
993 CreateMockRead(body2, 8), | |
994 CreateMockRead(fbody2, 9), | |
995 CreateMockRead(resp3, 11), | |
996 CreateMockRead(body3, 12), | |
997 CreateMockRead(fbody3, 13), | |
998 | |
999 MockRead(ASYNC, 0, 14), // EOF | |
1000 }; | |
1001 | |
1002 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1003 | |
1004 NetLogWithSource log; | |
1005 TransactionHelperResult out; | |
1006 { | |
1007 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
1008 NetLogWithSource(), nullptr); | |
1009 helper.RunPreTestSetup(); | |
1010 helper.AddData(&data); | |
1011 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
1012 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
1013 HttpNetworkTransaction trans3(DEFAULT_PRIORITY, helper.session()); | |
1014 | |
1015 TestCompletionCallback callback1; | |
1016 TestCompletionCallback callback2; | |
1017 TestCompletionCallback callback3; | |
1018 | |
1019 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
1020 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
1021 HttpRequestInfo httpreq3 = CreateGetRequest(); | |
1022 | |
1023 out.rv = trans1.Start(&httpreq1, callback1.callback(), log); | |
1024 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
1025 // Run transaction 1 through quickly to force a read of our SETTINGS | |
1026 // frame. | |
1027 out.rv = callback1.WaitForResult(); | |
1028 ASSERT_THAT(out.rv, IsOk()); | |
1029 | |
1030 out.rv = trans2.Start(&httpreq2, callback2.callback(), log); | |
1031 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
1032 out.rv = trans3.Start(&httpreq3, callback3.callback(), log); | |
1033 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
1034 out.rv = callback2.WaitForResult(); | |
1035 ASSERT_THAT(out.rv, IsOk()); | |
1036 | |
1037 out.rv = callback3.WaitForResult(); | |
1038 ASSERT_THAT(out.rv, IsOk()); | |
1039 | |
1040 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
1041 ASSERT_TRUE(response1); | |
1042 EXPECT_TRUE(response1->headers); | |
1043 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
1044 out.status_line = response1->headers->GetStatusLine(); | |
1045 out.response_info = *response1; | |
1046 out.rv = ReadTransaction(&trans1, &out.response_data); | |
1047 EXPECT_THAT(out.rv, IsOk()); | |
1048 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1049 EXPECT_EQ("hello!hello!", out.response_data); | |
1050 | |
1051 const HttpResponseInfo* response2 = trans2.GetResponseInfo(); | |
1052 out.status_line = response2->headers->GetStatusLine(); | |
1053 out.response_info = *response2; | |
1054 out.rv = ReadTransaction(&trans2, &out.response_data); | |
1055 EXPECT_THAT(out.rv, IsOk()); | |
1056 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1057 EXPECT_EQ("hello!hello!", out.response_data); | |
1058 | |
1059 const HttpResponseInfo* response3 = trans3.GetResponseInfo(); | |
1060 out.status_line = response3->headers->GetStatusLine(); | |
1061 out.response_info = *response3; | |
1062 out.rv = ReadTransaction(&trans3, &out.response_data); | |
1063 EXPECT_THAT(out.rv, IsOk()); | |
1064 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1065 EXPECT_EQ("hello!hello!", out.response_data); | |
1066 | |
1067 helper.VerifyDataConsumed(); | |
1068 } | |
1069 EXPECT_THAT(out.rv, IsOk()); | |
1070 } | |
1071 | |
1072 // Similar to ThreeGetsWithMaxConcurrent above, however this test adds | |
1073 // a fourth transaction. The third and fourth transactions have | |
1074 // different data ("hello!" vs "hello!hello!") and because of the | |
1075 // user specified priority, we expect to see them inverted in | |
1076 // the response from the server. | |
1077 TEST_F(SpdyNetworkTransactionTest, FourGetsWithMaxConcurrentPriority) { | |
1078 // Construct the request. | |
1079 SpdySerializedFrame req( | |
1080 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
1081 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
1082 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
1083 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1084 spdy_util_.UpdateWithStreamDestruction(1); | |
1085 | |
1086 SpdySerializedFrame req2( | |
1087 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
1088 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
1089 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
1090 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
1091 spdy_util_.UpdateWithStreamDestruction(3); | |
1092 | |
1093 SpdySerializedFrame req4( | |
1094 spdy_util_.ConstructSpdyGet(nullptr, 0, 5, HIGHEST, true)); | |
1095 SpdySerializedFrame resp4(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5)); | |
1096 SpdySerializedFrame fbody4(spdy_util_.ConstructSpdyDataFrame(5, true)); | |
1097 spdy_util_.UpdateWithStreamDestruction(5); | |
1098 | |
1099 SpdySerializedFrame req3( | |
1100 spdy_util_.ConstructSpdyGet(nullptr, 0, 7, LOWEST, true)); | |
1101 SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 7)); | |
1102 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(7, false)); | |
1103 SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(7, true)); | |
1104 | |
1105 SettingsMap settings; | |
1106 const uint32_t max_concurrent_streams = 1; | |
1107 settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; | |
1108 SpdySerializedFrame settings_frame( | |
1109 spdy_util_.ConstructSpdySettings(settings)); | |
1110 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
1111 MockWrite writes[] = { | |
1112 CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5), | |
1113 // By making these synchronous, it guarantees that they are not *started* | |
1114 // before their sequence number, which in turn verifies that only a single | |
1115 // request is in-flight at a time. | |
1116 CreateMockWrite(req2, 6, SYNCHRONOUS), | |
1117 CreateMockWrite(req4, 10, SYNCHRONOUS), | |
1118 CreateMockWrite(req3, 13, SYNCHRONOUS), | |
1119 }; | |
1120 MockRead reads[] = { | |
1121 CreateMockRead(settings_frame, 1), | |
1122 CreateMockRead(resp, 2), | |
1123 CreateMockRead(body, 3), | |
1124 CreateMockRead(fbody, 4), | |
1125 CreateMockRead(resp2, 7), | |
1126 CreateMockRead(body2, 8), | |
1127 CreateMockRead(fbody2, 9), | |
1128 CreateMockRead(resp4, 11), | |
1129 CreateMockRead(fbody4, 12), | |
1130 CreateMockRead(resp3, 14), | |
1131 CreateMockRead(body3, 15), | |
1132 CreateMockRead(fbody3, 16), | |
1133 | |
1134 MockRead(ASYNC, 0, 17), // EOF | |
1135 }; | |
1136 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1137 NetLogWithSource log; | |
1138 TransactionHelperResult out; | |
1139 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
1140 NetLogWithSource(), nullptr); | |
1141 helper.RunPreTestSetup(); | |
1142 helper.AddData(&data); | |
1143 | |
1144 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
1145 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
1146 HttpNetworkTransaction trans3(DEFAULT_PRIORITY, helper.session()); | |
1147 HttpNetworkTransaction trans4(HIGHEST, helper.session()); | |
1148 | |
1149 TestCompletionCallback callback1; | |
1150 TestCompletionCallback callback2; | |
1151 TestCompletionCallback callback3; | |
1152 TestCompletionCallback callback4; | |
1153 | |
1154 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
1155 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
1156 HttpRequestInfo httpreq3 = CreateGetRequest(); | |
1157 HttpRequestInfo httpreq4 = CreateGetRequest(); | |
1158 | |
1159 out.rv = trans1.Start(&httpreq1, callback1.callback(), log); | |
1160 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
1161 // Run transaction 1 through quickly to force a read of our SETTINGS frame. | |
1162 out.rv = callback1.WaitForResult(); | |
1163 ASSERT_THAT(out.rv, IsOk()); | |
1164 | |
1165 // Finish async network reads and writes associated with |trans1|. | |
1166 base::RunLoop().RunUntilIdle(); | |
1167 | |
1168 out.rv = trans2.Start(&httpreq2, callback2.callback(), log); | |
1169 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
1170 out.rv = trans3.Start(&httpreq3, callback3.callback(), log); | |
1171 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
1172 out.rv = trans4.Start(&httpreq4, callback4.callback(), log); | |
1173 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
1174 | |
1175 out.rv = callback2.WaitForResult(); | |
1176 ASSERT_THAT(out.rv, IsOk()); | |
1177 | |
1178 out.rv = callback3.WaitForResult(); | |
1179 ASSERT_THAT(out.rv, IsOk()); | |
1180 | |
1181 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
1182 EXPECT_TRUE(response1->headers); | |
1183 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
1184 out.status_line = response1->headers->GetStatusLine(); | |
1185 out.response_info = *response1; | |
1186 out.rv = ReadTransaction(&trans1, &out.response_data); | |
1187 EXPECT_THAT(out.rv, IsOk()); | |
1188 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1189 EXPECT_EQ("hello!hello!", out.response_data); | |
1190 | |
1191 const HttpResponseInfo* response2 = trans2.GetResponseInfo(); | |
1192 out.status_line = response2->headers->GetStatusLine(); | |
1193 out.response_info = *response2; | |
1194 out.rv = ReadTransaction(&trans2, &out.response_data); | |
1195 EXPECT_THAT(out.rv, IsOk()); | |
1196 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1197 EXPECT_EQ("hello!hello!", out.response_data); | |
1198 | |
1199 // notice: response3 gets two hellos, response4 gets one | |
1200 // hello, so we know dequeuing priority was respected. | |
1201 const HttpResponseInfo* response3 = trans3.GetResponseInfo(); | |
1202 out.status_line = response3->headers->GetStatusLine(); | |
1203 out.response_info = *response3; | |
1204 out.rv = ReadTransaction(&trans3, &out.response_data); | |
1205 EXPECT_THAT(out.rv, IsOk()); | |
1206 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1207 EXPECT_EQ("hello!hello!", out.response_data); | |
1208 | |
1209 out.rv = callback4.WaitForResult(); | |
1210 EXPECT_THAT(out.rv, IsOk()); | |
1211 const HttpResponseInfo* response4 = trans4.GetResponseInfo(); | |
1212 out.status_line = response4->headers->GetStatusLine(); | |
1213 out.response_info = *response4; | |
1214 out.rv = ReadTransaction(&trans4, &out.response_data); | |
1215 EXPECT_THAT(out.rv, IsOk()); | |
1216 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1217 EXPECT_EQ("hello!", out.response_data); | |
1218 helper.VerifyDataConsumed(); | |
1219 EXPECT_THAT(out.rv, IsOk()); | |
1220 } | |
1221 | |
1222 // Similar to ThreeGetsMaxConcurrrent above, however, this test | |
1223 // deletes a session in the middle of the transaction to ensure | |
1224 // that we properly remove pendingcreatestream objects from | |
1225 // the spdy_session | |
1226 TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentDelete) { | |
1227 // Construct the request. | |
1228 SpdySerializedFrame req( | |
1229 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
1230 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
1231 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
1232 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1233 spdy_util_.UpdateWithStreamDestruction(1); | |
1234 | |
1235 SpdySerializedFrame req2( | |
1236 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
1237 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
1238 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
1239 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
1240 | |
1241 SettingsMap settings; | |
1242 const uint32_t max_concurrent_streams = 1; | |
1243 settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; | |
1244 SpdySerializedFrame settings_frame( | |
1245 spdy_util_.ConstructSpdySettings(settings)); | |
1246 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
1247 | |
1248 MockWrite writes[] = { | |
1249 CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5), | |
1250 CreateMockWrite(req2, 6), | |
1251 }; | |
1252 MockRead reads[] = { | |
1253 CreateMockRead(settings_frame, 1), CreateMockRead(resp, 2), | |
1254 CreateMockRead(body, 3), CreateMockRead(fbody, 4), | |
1255 CreateMockRead(resp2, 7), CreateMockRead(body2, 8), | |
1256 CreateMockRead(fbody2, 9), MockRead(ASYNC, 0, 10), // EOF | |
1257 }; | |
1258 | |
1259 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1260 | |
1261 NetLogWithSource log; | |
1262 TransactionHelperResult out; | |
1263 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
1264 NetLogWithSource(), nullptr); | |
1265 helper.RunPreTestSetup(); | |
1266 helper.AddData(&data); | |
1267 std::unique_ptr<HttpNetworkTransaction> trans1( | |
1268 new HttpNetworkTransaction(DEFAULT_PRIORITY, helper.session())); | |
1269 std::unique_ptr<HttpNetworkTransaction> trans2( | |
1270 new HttpNetworkTransaction(DEFAULT_PRIORITY, helper.session())); | |
1271 std::unique_ptr<HttpNetworkTransaction> trans3( | |
1272 new HttpNetworkTransaction(DEFAULT_PRIORITY, helper.session())); | |
1273 | |
1274 TestCompletionCallback callback1; | |
1275 TestCompletionCallback callback2; | |
1276 TestCompletionCallback callback3; | |
1277 | |
1278 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
1279 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
1280 HttpRequestInfo httpreq3 = CreateGetRequest(); | |
1281 | |
1282 out.rv = trans1->Start(&httpreq1, callback1.callback(), log); | |
1283 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
1284 // Run transaction 1 through quickly to force a read of our SETTINGS frame. | |
1285 out.rv = callback1.WaitForResult(); | |
1286 ASSERT_THAT(out.rv, IsOk()); | |
1287 | |
1288 out.rv = trans2->Start(&httpreq2, callback2.callback(), log); | |
1289 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
1290 out.rv = trans3->Start(&httpreq3, callback3.callback(), log); | |
1291 trans3.reset(); | |
1292 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
1293 out.rv = callback2.WaitForResult(); | |
1294 ASSERT_THAT(out.rv, IsOk()); | |
1295 | |
1296 const HttpResponseInfo* response1 = trans1->GetResponseInfo(); | |
1297 ASSERT_TRUE(response1); | |
1298 EXPECT_TRUE(response1->headers); | |
1299 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
1300 out.status_line = response1->headers->GetStatusLine(); | |
1301 out.response_info = *response1; | |
1302 out.rv = ReadTransaction(trans1.get(), &out.response_data); | |
1303 EXPECT_THAT(out.rv, IsOk()); | |
1304 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1305 EXPECT_EQ("hello!hello!", out.response_data); | |
1306 | |
1307 const HttpResponseInfo* response2 = trans2->GetResponseInfo(); | |
1308 ASSERT_TRUE(response2); | |
1309 out.status_line = response2->headers->GetStatusLine(); | |
1310 out.response_info = *response2; | |
1311 out.rv = ReadTransaction(trans2.get(), &out.response_data); | |
1312 EXPECT_THAT(out.rv, IsOk()); | |
1313 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1314 EXPECT_EQ("hello!hello!", out.response_data); | |
1315 helper.VerifyDataConsumed(); | |
1316 EXPECT_THAT(out.rv, IsOk()); | |
1317 } | |
1318 | |
1319 namespace { | |
1320 | |
1321 // The KillerCallback will delete the transaction on error as part of the | |
1322 // callback. | |
1323 class KillerCallback : public TestCompletionCallbackBase { | |
1324 public: | |
1325 explicit KillerCallback(HttpNetworkTransaction* transaction) | |
1326 : transaction_(transaction), | |
1327 callback_(base::Bind(&KillerCallback::OnComplete, | |
1328 base::Unretained(this))) { | |
1329 } | |
1330 | |
1331 ~KillerCallback() override {} | |
1332 | |
1333 const CompletionCallback& callback() const { return callback_; } | |
1334 | |
1335 private: | |
1336 void OnComplete(int result) { | |
1337 if (result < 0) | |
1338 delete transaction_; | |
1339 | |
1340 SetResult(result); | |
1341 } | |
1342 | |
1343 HttpNetworkTransaction* transaction_; | |
1344 CompletionCallback callback_; | |
1345 }; | |
1346 | |
1347 } // namespace | |
1348 | |
1349 // Similar to ThreeGetsMaxConcurrrentDelete above, however, this test | |
1350 // closes the socket while we have a pending transaction waiting for | |
1351 // a pending stream creation. http://crbug.com/52901 | |
1352 TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentSocketClose) { | |
1353 // Construct the request. | |
1354 SpdySerializedFrame req( | |
1355 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
1356 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
1357 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
1358 SpdySerializedFrame fin_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1359 spdy_util_.UpdateWithStreamDestruction(1); | |
1360 | |
1361 SpdySerializedFrame req2( | |
1362 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
1363 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
1364 | |
1365 SettingsMap settings; | |
1366 const uint32_t max_concurrent_streams = 1; | |
1367 settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; | |
1368 SpdySerializedFrame settings_frame( | |
1369 spdy_util_.ConstructSpdySettings(settings)); | |
1370 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
1371 | |
1372 MockWrite writes[] = { | |
1373 CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5), | |
1374 CreateMockWrite(req2, 6), | |
1375 }; | |
1376 MockRead reads[] = { | |
1377 CreateMockRead(settings_frame, 1), | |
1378 CreateMockRead(resp, 2), | |
1379 CreateMockRead(body, 3), | |
1380 CreateMockRead(fin_body, 4), | |
1381 CreateMockRead(resp2, 7), | |
1382 MockRead(ASYNC, ERR_CONNECTION_RESET, 8), // Abort! | |
1383 }; | |
1384 | |
1385 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1386 SequencedSocketData data_placeholder(nullptr, 0, nullptr, 0); | |
1387 | |
1388 NetLogWithSource log; | |
1389 TransactionHelperResult out; | |
1390 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
1391 NetLogWithSource(), nullptr); | |
1392 helper.RunPreTestSetup(); | |
1393 helper.AddData(&data); | |
1394 // We require placeholder data because three get requests are sent out, so | |
1395 // there needs to be three sets of SSL connection data. | |
1396 helper.AddData(&data_placeholder); | |
1397 helper.AddData(&data_placeholder); | |
1398 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
1399 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
1400 HttpNetworkTransaction* trans3( | |
1401 new HttpNetworkTransaction(DEFAULT_PRIORITY, helper.session())); | |
1402 | |
1403 TestCompletionCallback callback1; | |
1404 TestCompletionCallback callback2; | |
1405 KillerCallback callback3(trans3); | |
1406 | |
1407 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
1408 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
1409 HttpRequestInfo httpreq3 = CreateGetRequest(); | |
1410 | |
1411 out.rv = trans1.Start(&httpreq1, callback1.callback(), log); | |
1412 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
1413 // Run transaction 1 through quickly to force a read of our SETTINGS frame. | |
1414 out.rv = callback1.WaitForResult(); | |
1415 ASSERT_THAT(out.rv, IsOk()); | |
1416 | |
1417 out.rv = trans2.Start(&httpreq2, callback2.callback(), log); | |
1418 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
1419 out.rv = trans3->Start(&httpreq3, callback3.callback(), log); | |
1420 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
1421 out.rv = callback3.WaitForResult(); | |
1422 ASSERT_THAT(out.rv, IsError(ERR_ABORTED)); | |
1423 | |
1424 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
1425 ASSERT_TRUE(response1); | |
1426 EXPECT_TRUE(response1->headers); | |
1427 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
1428 out.status_line = response1->headers->GetStatusLine(); | |
1429 out.response_info = *response1; | |
1430 out.rv = ReadTransaction(&trans1, &out.response_data); | |
1431 EXPECT_THAT(out.rv, IsOk()); | |
1432 | |
1433 const HttpResponseInfo* response2 = trans2.GetResponseInfo(); | |
1434 ASSERT_TRUE(response2); | |
1435 out.status_line = response2->headers->GetStatusLine(); | |
1436 out.response_info = *response2; | |
1437 out.rv = ReadTransaction(&trans2, &out.response_data); | |
1438 EXPECT_THAT(out.rv, IsError(ERR_CONNECTION_RESET)); | |
1439 | |
1440 helper.VerifyDataConsumed(); | |
1441 } | |
1442 | |
1443 // Test that a simple PUT request works. | |
1444 TEST_F(SpdyNetworkTransactionTest, Put) { | |
1445 // Setup the request | |
1446 HttpRequestInfo request; | |
1447 request.method = "PUT"; | |
1448 request.url = default_url_; | |
1449 | |
1450 SpdyHeaderBlock put_headers( | |
1451 spdy_util_.ConstructPutHeaderBlock(kDefaultUrl, 0)); | |
1452 SpdySerializedFrame req( | |
1453 spdy_util_.ConstructSpdyHeaders(1, std::move(put_headers), LOWEST, true)); | |
1454 MockWrite writes[] = { | |
1455 CreateMockWrite(req, 0), | |
1456 }; | |
1457 | |
1458 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
1459 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1460 MockRead reads[] = { | |
1461 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
1462 MockRead(ASYNC, 0, 3) // EOF | |
1463 }; | |
1464 | |
1465 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1466 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
1467 NetLogWithSource(), nullptr); | |
1468 helper.RunToCompletion(&data); | |
1469 TransactionHelperResult out = helper.output(); | |
1470 | |
1471 EXPECT_THAT(out.rv, IsOk()); | |
1472 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1473 } | |
1474 | |
1475 // Test that a simple HEAD request works. | |
1476 TEST_F(SpdyNetworkTransactionTest, Head) { | |
1477 // Setup the request | |
1478 HttpRequestInfo request; | |
1479 request.method = "HEAD"; | |
1480 request.url = default_url_; | |
1481 | |
1482 SpdyHeaderBlock head_headers( | |
1483 spdy_util_.ConstructHeadHeaderBlock(kDefaultUrl, 0)); | |
1484 SpdySerializedFrame req(spdy_util_.ConstructSpdyHeaders( | |
1485 1, std::move(head_headers), LOWEST, true)); | |
1486 MockWrite writes[] = { | |
1487 CreateMockWrite(req, 0), | |
1488 }; | |
1489 | |
1490 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
1491 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1492 MockRead reads[] = { | |
1493 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
1494 MockRead(ASYNC, 0, 3) // EOF | |
1495 }; | |
1496 | |
1497 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1498 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
1499 NetLogWithSource(), nullptr); | |
1500 helper.RunToCompletion(&data); | |
1501 TransactionHelperResult out = helper.output(); | |
1502 | |
1503 EXPECT_THAT(out.rv, IsOk()); | |
1504 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1505 } | |
1506 | |
1507 // Test that a simple POST works. | |
1508 TEST_F(SpdyNetworkTransactionTest, Post) { | |
1509 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
1510 kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0)); | |
1511 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1512 MockWrite writes[] = { | |
1513 CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame | |
1514 }; | |
1515 | |
1516 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
1517 MockRead reads[] = { | |
1518 CreateMockRead(resp, 2), CreateMockRead(body, 3), | |
1519 MockRead(ASYNC, 0, 4) // EOF | |
1520 }; | |
1521 | |
1522 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1523 NormalSpdyTransactionHelper helper(CreatePostRequest(), DEFAULT_PRIORITY, | |
1524 NetLogWithSource(), nullptr); | |
1525 helper.RunToCompletion(&data); | |
1526 TransactionHelperResult out = helper.output(); | |
1527 EXPECT_THAT(out.rv, IsOk()); | |
1528 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1529 EXPECT_EQ("hello!", out.response_data); | |
1530 } | |
1531 | |
1532 // Test that a POST with a file works. | |
1533 TEST_F(SpdyNetworkTransactionTest, FilePost) { | |
1534 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
1535 kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0)); | |
1536 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1537 MockWrite writes[] = { | |
1538 CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame | |
1539 }; | |
1540 | |
1541 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
1542 MockRead reads[] = { | |
1543 CreateMockRead(resp, 2), CreateMockRead(body, 3), | |
1544 MockRead(ASYNC, 0, 4) // EOF | |
1545 }; | |
1546 | |
1547 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1548 NormalSpdyTransactionHelper helper(CreateFilePostRequest(), DEFAULT_PRIORITY, | |
1549 NetLogWithSource(), nullptr); | |
1550 helper.RunToCompletion(&data); | |
1551 TransactionHelperResult out = helper.output(); | |
1552 EXPECT_THAT(out.rv, IsOk()); | |
1553 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1554 EXPECT_EQ("hello!", out.response_data); | |
1555 } | |
1556 | |
1557 // Test that a POST with a unreadable file fails. | |
1558 TEST_F(SpdyNetworkTransactionTest, UnreadableFilePost) { | |
1559 MockWrite writes[] = { | |
1560 MockWrite(ASYNC, 0, 0) // EOF | |
1561 }; | |
1562 MockRead reads[] = { | |
1563 MockRead(ASYNC, 0, 1) // EOF | |
1564 }; | |
1565 | |
1566 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1567 NormalSpdyTransactionHelper helper(CreateUnreadableFilePostRequest(), | |
1568 DEFAULT_PRIORITY, NetLogWithSource(), | |
1569 nullptr); | |
1570 helper.RunPreTestSetup(); | |
1571 helper.AddData(&data); | |
1572 helper.RunDefaultTest(); | |
1573 | |
1574 base::RunLoop().RunUntilIdle(); | |
1575 helper.VerifyDataNotConsumed(); | |
1576 EXPECT_THAT(helper.output().rv, IsError(ERR_ACCESS_DENIED)); | |
1577 } | |
1578 | |
1579 // Test that a complex POST works. | |
1580 TEST_F(SpdyNetworkTransactionTest, ComplexPost) { | |
1581 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
1582 kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0)); | |
1583 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1584 MockWrite writes[] = { | |
1585 CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame | |
1586 }; | |
1587 | |
1588 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
1589 MockRead reads[] = { | |
1590 CreateMockRead(resp, 2), CreateMockRead(body, 3), | |
1591 MockRead(ASYNC, 0, 4) // EOF | |
1592 }; | |
1593 | |
1594 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1595 NormalSpdyTransactionHelper helper(CreateComplexPostRequest(), | |
1596 DEFAULT_PRIORITY, NetLogWithSource(), | |
1597 nullptr); | |
1598 helper.RunToCompletion(&data); | |
1599 TransactionHelperResult out = helper.output(); | |
1600 EXPECT_THAT(out.rv, IsOk()); | |
1601 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1602 EXPECT_EQ("hello!", out.response_data); | |
1603 } | |
1604 | |
1605 // Test that a chunked POST works. | |
1606 TEST_F(SpdyNetworkTransactionTest, ChunkedPost) { | |
1607 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
1608 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1609 MockWrite writes[] = { | |
1610 CreateMockWrite(req, 0), CreateMockWrite(body, 1), | |
1611 }; | |
1612 | |
1613 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
1614 MockRead reads[] = { | |
1615 CreateMockRead(resp, 2), CreateMockRead(body, 3), | |
1616 MockRead(ASYNC, 0, 4) // EOF | |
1617 }; | |
1618 | |
1619 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1620 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
1621 DEFAULT_PRIORITY, NetLogWithSource(), | |
1622 nullptr); | |
1623 | |
1624 // These chunks get merged into a single frame when being sent. | |
1625 const int kFirstChunkSize = kUploadDataSize/2; | |
1626 upload_chunked_data_stream()->AppendData(kUploadData, kFirstChunkSize, false); | |
1627 upload_chunked_data_stream()->AppendData( | |
1628 kUploadData + kFirstChunkSize, kUploadDataSize - kFirstChunkSize, true); | |
1629 | |
1630 helper.RunToCompletion(&data); | |
1631 TransactionHelperResult out = helper.output(); | |
1632 EXPECT_THAT(out.rv, IsOk()); | |
1633 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1634 EXPECT_EQ(kUploadData, out.response_data); | |
1635 } | |
1636 | |
1637 // Test that a chunked POST works with chunks appended after transaction starts. | |
1638 TEST_F(SpdyNetworkTransactionTest, DelayedChunkedPost) { | |
1639 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
1640 SpdySerializedFrame chunk1(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
1641 SpdySerializedFrame chunk2(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
1642 SpdySerializedFrame chunk3(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1643 MockWrite writes[] = { | |
1644 CreateMockWrite(req, 0), CreateMockWrite(chunk1, 1), | |
1645 CreateMockWrite(chunk2, 2), CreateMockWrite(chunk3, 3), | |
1646 }; | |
1647 | |
1648 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
1649 MockRead reads[] = { | |
1650 CreateMockRead(resp, 4), CreateMockRead(chunk1, 5), | |
1651 CreateMockRead(chunk2, 6), CreateMockRead(chunk3, 7), | |
1652 MockRead(ASYNC, 0, 8) // EOF | |
1653 }; | |
1654 | |
1655 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1656 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
1657 DEFAULT_PRIORITY, NetLogWithSource(), | |
1658 nullptr); | |
1659 | |
1660 upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, false); | |
1661 | |
1662 helper.RunPreTestSetup(); | |
1663 helper.AddData(&data); | |
1664 ASSERT_TRUE(helper.StartDefaultTest()); | |
1665 | |
1666 base::RunLoop().RunUntilIdle(); | |
1667 upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, false); | |
1668 base::RunLoop().RunUntilIdle(); | |
1669 upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, true); | |
1670 | |
1671 helper.FinishDefaultTest(); | |
1672 helper.VerifyDataConsumed(); | |
1673 | |
1674 SpdyString expected_response; | |
1675 expected_response += kUploadData; | |
1676 expected_response += kUploadData; | |
1677 expected_response += kUploadData; | |
1678 | |
1679 TransactionHelperResult out = helper.output(); | |
1680 EXPECT_THAT(out.rv, IsOk()); | |
1681 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1682 EXPECT_EQ(expected_response, out.response_data); | |
1683 } | |
1684 | |
1685 // Test that a POST without any post data works. | |
1686 TEST_F(SpdyNetworkTransactionTest, NullPost) { | |
1687 // Setup the request | |
1688 HttpRequestInfo request; | |
1689 request.method = "POST"; | |
1690 request.url = default_url_; | |
1691 // Create an empty UploadData. | |
1692 request.upload_data_stream = nullptr; | |
1693 | |
1694 // When request.upload_data_stream is NULL for post, content-length is | |
1695 // expected to be 0. | |
1696 SpdyHeaderBlock req_block( | |
1697 spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0)); | |
1698 SpdySerializedFrame req( | |
1699 spdy_util_.ConstructSpdyHeaders(1, std::move(req_block), LOWEST, true)); | |
1700 | |
1701 MockWrite writes[] = { | |
1702 CreateMockWrite(req, 0), | |
1703 }; | |
1704 | |
1705 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
1706 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1707 MockRead reads[] = { | |
1708 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
1709 MockRead(ASYNC, 0, 3) // EOF | |
1710 }; | |
1711 | |
1712 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1713 | |
1714 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
1715 NetLogWithSource(), nullptr); | |
1716 helper.RunToCompletion(&data); | |
1717 TransactionHelperResult out = helper.output(); | |
1718 EXPECT_THAT(out.rv, IsOk()); | |
1719 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1720 EXPECT_EQ("hello!", out.response_data); | |
1721 } | |
1722 | |
1723 // Test that a simple POST works. | |
1724 TEST_F(SpdyNetworkTransactionTest, EmptyPost) { | |
1725 // Create an empty UploadDataStream. | |
1726 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
1727 ElementsUploadDataStream stream(std::move(element_readers), 0); | |
1728 | |
1729 // Setup the request | |
1730 HttpRequestInfo request; | |
1731 request.method = "POST"; | |
1732 request.url = default_url_; | |
1733 request.upload_data_stream = &stream; | |
1734 | |
1735 const uint64_t kContentLength = 0; | |
1736 | |
1737 SpdyHeaderBlock req_block( | |
1738 spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, kContentLength)); | |
1739 SpdySerializedFrame req( | |
1740 spdy_util_.ConstructSpdyHeaders(1, std::move(req_block), LOWEST, true)); | |
1741 | |
1742 MockWrite writes[] = { | |
1743 CreateMockWrite(req, 0), | |
1744 }; | |
1745 | |
1746 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
1747 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1748 MockRead reads[] = { | |
1749 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
1750 MockRead(ASYNC, 0, 3) // EOF | |
1751 }; | |
1752 | |
1753 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1754 | |
1755 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
1756 NetLogWithSource(), nullptr); | |
1757 helper.RunToCompletion(&data); | |
1758 TransactionHelperResult out = helper.output(); | |
1759 EXPECT_THAT(out.rv, IsOk()); | |
1760 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1761 EXPECT_EQ("hello!", out.response_data); | |
1762 } | |
1763 | |
1764 // While we're doing a post, the server sends the reply before upload completes. | |
1765 TEST_F(SpdyNetworkTransactionTest, ResponseBeforePostCompletes) { | |
1766 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
1767 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1768 MockWrite writes[] = { | |
1769 CreateMockWrite(req, 0), CreateMockWrite(body, 3), | |
1770 }; | |
1771 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
1772 MockRead reads[] = { | |
1773 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
1774 MockRead(ASYNC, 0, 4) // EOF | |
1775 }; | |
1776 | |
1777 // Write the request headers, and read the complete response | |
1778 // while still waiting for chunked request data. | |
1779 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1780 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
1781 DEFAULT_PRIORITY, NetLogWithSource(), | |
1782 nullptr); | |
1783 helper.RunPreTestSetup(); | |
1784 helper.AddData(&data); | |
1785 | |
1786 ASSERT_TRUE(helper.StartDefaultTest()); | |
1787 | |
1788 base::RunLoop().RunUntilIdle(); | |
1789 | |
1790 // Process the request headers, response headers, and response body. | |
1791 // The request body is still in flight. | |
1792 const HttpResponseInfo* response = helper.trans()->GetResponseInfo(); | |
1793 EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); | |
1794 | |
1795 // Finish sending the request body. | |
1796 upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, true); | |
1797 helper.WaitForCallbackToComplete(); | |
1798 EXPECT_THAT(helper.output().rv, IsOk()); | |
1799 | |
1800 SpdyString response_body; | |
1801 EXPECT_THAT(ReadTransaction(helper.trans(), &response_body), IsOk()); | |
1802 EXPECT_EQ(kUploadData, response_body); | |
1803 | |
1804 // Finish async network reads/writes. | |
1805 base::RunLoop().RunUntilIdle(); | |
1806 helper.VerifyDataConsumed(); | |
1807 } | |
1808 | |
1809 // The client upon cancellation tries to send a RST_STREAM frame. The mock | |
1810 // socket causes the TCP write to return zero. This test checks that the client | |
1811 // tries to queue up the RST_STREAM frame again. | |
1812 TEST_F(SpdyNetworkTransactionTest, SocketWriteReturnsZero) { | |
1813 SpdySerializedFrame req( | |
1814 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
1815 SpdySerializedFrame rst( | |
1816 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_CANCEL)); | |
1817 MockWrite writes[] = { | |
1818 CreateMockWrite(req, 0, SYNCHRONOUS), MockWrite(SYNCHRONOUS, 0, 0, 2), | |
1819 CreateMockWrite(rst, 3, SYNCHRONOUS), | |
1820 }; | |
1821 | |
1822 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
1823 MockRead reads[] = { | |
1824 CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 0, 4) // EOF | |
1825 }; | |
1826 | |
1827 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1828 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
1829 NetLogWithSource(), nullptr); | |
1830 helper.RunPreTestSetup(); | |
1831 helper.AddData(&data); | |
1832 helper.StartDefaultTest(); | |
1833 EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING)); | |
1834 | |
1835 helper.WaitForCallbackToComplete(); | |
1836 EXPECT_THAT(helper.output().rv, IsOk()); | |
1837 | |
1838 helper.ResetTrans(); | |
1839 base::RunLoop().RunUntilIdle(); | |
1840 | |
1841 helper.VerifyDataConsumed(); | |
1842 } | |
1843 | |
1844 // Test that the transaction doesn't crash when we don't have a reply. | |
1845 TEST_F(SpdyNetworkTransactionTest, ResponseWithoutHeaders) { | |
1846 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1847 MockRead reads[] = { | |
1848 CreateMockRead(body, 1), MockRead(ASYNC, 0, 3) // EOF | |
1849 }; | |
1850 | |
1851 SpdySerializedFrame req( | |
1852 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
1853 SpdySerializedFrame rst( | |
1854 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
1855 MockWrite writes[] = { | |
1856 CreateMockWrite(req, 0), CreateMockWrite(rst, 2), | |
1857 }; | |
1858 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1859 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
1860 NetLogWithSource(), nullptr); | |
1861 helper.RunToCompletion(&data); | |
1862 TransactionHelperResult out = helper.output(); | |
1863 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
1864 } | |
1865 | |
1866 // Test that the transaction doesn't crash when we get two replies on the same | |
1867 // stream ID. See http://crbug.com/45639. | |
1868 TEST_F(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) { | |
1869 SpdySerializedFrame req( | |
1870 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
1871 SpdySerializedFrame rst( | |
1872 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
1873 MockWrite writes[] = { | |
1874 CreateMockWrite(req, 0), CreateMockWrite(rst, 4), | |
1875 }; | |
1876 | |
1877 SpdySerializedFrame resp0(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
1878 SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
1879 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1880 MockRead reads[] = { | |
1881 CreateMockRead(resp0, 1), CreateMockRead(resp1, 2), | |
1882 CreateMockRead(body, 3), MockRead(ASYNC, 0, 5) // EOF | |
1883 }; | |
1884 | |
1885 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1886 | |
1887 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
1888 NetLogWithSource(), nullptr); | |
1889 helper.RunPreTestSetup(); | |
1890 helper.AddData(&data); | |
1891 | |
1892 HttpNetworkTransaction* trans = helper.trans(); | |
1893 | |
1894 TestCompletionCallback callback; | |
1895 int rv = | |
1896 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
1897 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
1898 rv = callback.WaitForResult(); | |
1899 EXPECT_THAT(rv, IsOk()); | |
1900 | |
1901 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
1902 ASSERT_TRUE(response); | |
1903 EXPECT_TRUE(response->headers); | |
1904 EXPECT_TRUE(response->was_fetched_via_spdy); | |
1905 SpdyString response_data; | |
1906 rv = ReadTransaction(trans, &response_data); | |
1907 EXPECT_THAT(rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
1908 | |
1909 helper.VerifyDataConsumed(); | |
1910 } | |
1911 | |
1912 TEST_F(SpdyNetworkTransactionTest, ResetReplyWithTransferEncoding) { | |
1913 // Construct the request. | |
1914 SpdySerializedFrame req( | |
1915 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
1916 SpdySerializedFrame rst( | |
1917 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
1918 MockWrite writes[] = { | |
1919 CreateMockWrite(req, 0), CreateMockWrite(rst, 2), | |
1920 }; | |
1921 | |
1922 const char* const headers[] = { | |
1923 "transfer-encoding", "chunked" | |
1924 }; | |
1925 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(headers, 1, 1)); | |
1926 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1927 MockRead reads[] = { | |
1928 CreateMockRead(resp, 1), CreateMockRead(body, 3), | |
1929 MockRead(ASYNC, 0, 4) // EOF | |
1930 }; | |
1931 | |
1932 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1933 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
1934 NetLogWithSource(), nullptr); | |
1935 helper.RunToCompletion(&data); | |
1936 TransactionHelperResult out = helper.output(); | |
1937 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
1938 | |
1939 helper.session()->spdy_session_pool()->CloseAllSessions(); | |
1940 helper.VerifyDataConsumed(); | |
1941 } | |
1942 | |
1943 TEST_F(SpdyNetworkTransactionTest, ResetPushWithTransferEncoding) { | |
1944 // Construct the request. | |
1945 SpdySerializedFrame req( | |
1946 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
1947 SpdySerializedFrame priority( | |
1948 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
1949 SpdySerializedFrame rst( | |
1950 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_PROTOCOL_ERROR)); | |
1951 MockWrite writes[] = { | |
1952 CreateMockWrite(req, 0), CreateMockWrite(priority, 3), | |
1953 CreateMockWrite(rst, 5), | |
1954 }; | |
1955 | |
1956 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
1957 const char* const headers[] = { | |
1958 "transfer-encoding", "chunked" | |
1959 }; | |
1960 SpdySerializedFrame push( | |
1961 spdy_util_.ConstructSpdyPush(headers, arraysize(headers) / 2, 2, 1, | |
1962 GetDefaultUrlWithPath("/1").c_str())); | |
1963 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
1964 MockRead reads[] = { | |
1965 CreateMockRead(resp, 1), CreateMockRead(push, 2), CreateMockRead(body, 4), | |
1966 MockRead(ASYNC, 0, 6) // EOF | |
1967 }; | |
1968 | |
1969 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
1970 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
1971 NetLogWithSource(), nullptr); | |
1972 helper.RunToCompletion(&data); | |
1973 TransactionHelperResult out = helper.output(); | |
1974 EXPECT_THAT(out.rv, IsOk()); | |
1975 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
1976 EXPECT_EQ("hello!", out.response_data); | |
1977 | |
1978 helper.session()->spdy_session_pool()->CloseAllSessions(); | |
1979 helper.VerifyDataConsumed(); | |
1980 } | |
1981 | |
1982 TEST_F(SpdyNetworkTransactionTest, CancelledTransaction) { | |
1983 // Construct the request. | |
1984 SpdySerializedFrame req( | |
1985 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
1986 MockWrite writes[] = { | |
1987 CreateMockWrite(req), | |
1988 }; | |
1989 | |
1990 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
1991 MockRead reads[] = { | |
1992 CreateMockRead(resp), | |
1993 // This following read isn't used by the test, except during the | |
1994 // RunUntilIdle() call at the end since the SpdySession survives the | |
1995 // HttpNetworkTransaction and still tries to continue Read()'ing. Any | |
1996 // MockRead will do here. | |
1997 MockRead(ASYNC, 0, 0) // EOF | |
1998 }; | |
1999 | |
2000 StaticSocketDataProvider data(reads, arraysize(reads), | |
2001 writes, arraysize(writes)); | |
2002 | |
2003 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
2004 NetLogWithSource(), nullptr); | |
2005 helper.RunPreTestSetup(); | |
2006 helper.AddData(&data); | |
2007 HttpNetworkTransaction* trans = helper.trans(); | |
2008 | |
2009 TestCompletionCallback callback; | |
2010 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
2011 NetLogWithSource()); | |
2012 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
2013 helper.ResetTrans(); // Cancel the transaction. | |
2014 | |
2015 // Flush the MessageLoop while the SpdySessionDependencies (in particular, the | |
2016 // MockClientSocketFactory) are still alive. | |
2017 base::RunLoop().RunUntilIdle(); | |
2018 helper.VerifyDataNotConsumed(); | |
2019 } | |
2020 | |
2021 // Verify that the client sends a Rst Frame upon cancelling the stream. | |
2022 TEST_F(SpdyNetworkTransactionTest, CancelledTransactionSendRst) { | |
2023 SpdySerializedFrame req( | |
2024 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2025 SpdySerializedFrame rst( | |
2026 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_CANCEL)); | |
2027 MockWrite writes[] = { | |
2028 CreateMockWrite(req, 0, SYNCHRONOUS), | |
2029 CreateMockWrite(rst, 2, SYNCHRONOUS), | |
2030 }; | |
2031 | |
2032 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2033 MockRead reads[] = { | |
2034 CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 0, 3) // EOF | |
2035 }; | |
2036 | |
2037 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2038 | |
2039 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
2040 NetLogWithSource(), nullptr); | |
2041 helper.RunPreTestSetup(); | |
2042 helper.AddData(&data); | |
2043 HttpNetworkTransaction* trans = helper.trans(); | |
2044 | |
2045 TestCompletionCallback callback; | |
2046 | |
2047 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
2048 NetLogWithSource()); | |
2049 EXPECT_THAT(callback.GetResult(rv), IsOk()); | |
2050 | |
2051 helper.ResetTrans(); | |
2052 base::RunLoop().RunUntilIdle(); | |
2053 | |
2054 helper.VerifyDataConsumed(); | |
2055 } | |
2056 | |
2057 // Verify that the client can correctly deal with the user callback attempting | |
2058 // to start another transaction on a session that is closing down. See | |
2059 // http://crbug.com/47455 | |
2060 TEST_F(SpdyNetworkTransactionTest, StartTransactionOnReadCallback) { | |
2061 SpdySerializedFrame req( | |
2062 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2063 MockWrite writes[] = {CreateMockWrite(req)}; | |
2064 MockWrite writes2[] = {CreateMockWrite(req, 0), | |
2065 MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 3)}; | |
2066 | |
2067 // The indicated length of this frame is longer than its actual length. When | |
2068 // the session receives an empty frame after this one, it shuts down the | |
2069 // session, and calls the read callback with the incomplete data. | |
2070 const uint8_t kGetBodyFrame2[] = { | |
2071 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, | |
2072 0x07, 'h', 'e', 'l', 'l', 'o', '!', | |
2073 }; | |
2074 | |
2075 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2076 MockRead reads[] = { | |
2077 CreateMockRead(resp, 1), | |
2078 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause | |
2079 MockRead(ASYNC, reinterpret_cast<const char*>(kGetBodyFrame2), | |
2080 arraysize(kGetBodyFrame2), 3), | |
2081 MockRead(ASYNC, ERR_IO_PENDING, 4), // Force a pause | |
2082 MockRead(ASYNC, 0, 0, 5), // EOF | |
2083 }; | |
2084 MockRead reads2[] = { | |
2085 CreateMockRead(resp, 1), MockRead(ASYNC, 0, 0, 2), // EOF | |
2086 }; | |
2087 | |
2088 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2089 SequencedSocketData data2(reads2, arraysize(reads2), writes2, | |
2090 arraysize(writes2)); | |
2091 | |
2092 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
2093 NetLogWithSource(), nullptr); | |
2094 helper.RunPreTestSetup(); | |
2095 helper.AddData(&data); | |
2096 helper.AddData(&data2); | |
2097 HttpNetworkTransaction* trans = helper.trans(); | |
2098 | |
2099 // Start the transaction with basic parameters. | |
2100 TestCompletionCallback callback; | |
2101 int rv = | |
2102 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
2103 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
2104 rv = callback.WaitForResult(); | |
2105 | |
2106 const int kSize = 3000; | |
2107 scoped_refptr<IOBuffer> buf(new IOBuffer(kSize)); | |
2108 rv = trans->Read( | |
2109 buf.get(), kSize, | |
2110 base::Bind(&SpdyNetworkTransactionTest::StartTransactionCallback, | |
2111 helper.session(), default_url_)); | |
2112 ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); | |
2113 // This forces an err_IO_pending, which sets the callback. | |
2114 data.Resume(); | |
2115 data.RunUntilPaused(); | |
2116 | |
2117 // This finishes the read. | |
2118 data.Resume(); | |
2119 base::RunLoop().RunUntilIdle(); | |
2120 helper.VerifyDataConsumed(); | |
2121 } | |
2122 | |
2123 // Verify that the client can correctly deal with the user callback deleting the | |
2124 // transaction. Failures will usually be valgrind errors. See | |
2125 // http://crbug.com/46925 | |
2126 TEST_F(SpdyNetworkTransactionTest, DeleteSessionOnReadCallback) { | |
2127 SpdySerializedFrame req( | |
2128 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2129 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
2130 | |
2131 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2132 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2133 MockRead reads[] = { | |
2134 CreateMockRead(resp, 1), | |
2135 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause | |
2136 CreateMockRead(body, 3), MockRead(ASYNC, 0, 0, 4), // EOF | |
2137 }; | |
2138 | |
2139 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2140 | |
2141 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
2142 NetLogWithSource(), nullptr); | |
2143 helper.RunPreTestSetup(); | |
2144 helper.AddData(&data); | |
2145 HttpNetworkTransaction* trans = helper.trans(); | |
2146 | |
2147 // Start the transaction with basic parameters. | |
2148 TestCompletionCallback callback; | |
2149 int rv = | |
2150 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
2151 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
2152 rv = callback.WaitForResult(); | |
2153 | |
2154 // Setup a user callback which will delete the session, and clear out the | |
2155 // memory holding the stream object. Note that the callback deletes trans. | |
2156 const int kSize = 3000; | |
2157 scoped_refptr<IOBuffer> buf(new IOBuffer(kSize)); | |
2158 rv = trans->Read( | |
2159 buf.get(), | |
2160 kSize, | |
2161 base::Bind(&SpdyNetworkTransactionTest::DeleteSessionCallback, | |
2162 base::Unretained(&helper))); | |
2163 ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); | |
2164 data.Resume(); | |
2165 | |
2166 // Finish running rest of tasks. | |
2167 base::RunLoop().RunUntilIdle(); | |
2168 helper.VerifyDataConsumed(); | |
2169 } | |
2170 | |
2171 TEST_F(SpdyNetworkTransactionTest, TestRawHeaderSizeSuccessfullRequest) { | |
2172 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
2173 headers["user-agent"] = ""; | |
2174 headers["accept-encoding"] = "gzip, deflate"; | |
2175 | |
2176 SpdySerializedFrame req( | |
2177 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
2178 MockWrite writes[] = { | |
2179 CreateMockWrite(req, 0), | |
2180 }; | |
2181 | |
2182 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2183 | |
2184 SpdySerializedFrame response_body_frame( | |
2185 spdy_util_.ConstructSpdyDataFrame(1, "should not include", 18, true)); | |
2186 | |
2187 MockRead response_headers(CreateMockRead(resp, 1)); | |
2188 MockRead reads[] = { | |
2189 response_headers, CreateMockRead(response_body_frame, 2), | |
2190 MockRead(ASYNC, 0, 0, 3) // EOF | |
2191 }; | |
2192 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2193 | |
2194 TestDelegate delegate; | |
2195 SpdyURLRequestContext spdy_url_request_context; | |
2196 TestNetworkDelegate network_delegate; | |
2197 spdy_url_request_context.set_network_delegate(&network_delegate); | |
2198 SSLSocketDataProvider ssl_data(ASYNC, OK); | |
2199 ssl_data.next_proto = kProtoHTTP2; | |
2200 | |
2201 std::unique_ptr<URLRequest> request(spdy_url_request_context.CreateRequest( | |
2202 GURL(kDefaultUrl), DEFAULT_PRIORITY, &delegate)); | |
2203 spdy_url_request_context.socket_factory().AddSSLSocketDataProvider(&ssl_data); | |
2204 spdy_url_request_context.socket_factory().AddSocketDataProvider(&data); | |
2205 | |
2206 request->Start(); | |
2207 base::RunLoop().Run(); | |
2208 | |
2209 EXPECT_LT(0, request->GetTotalSentBytes()); | |
2210 EXPECT_LT(0, request->GetTotalReceivedBytes()); | |
2211 EXPECT_EQ(network_delegate.total_network_bytes_sent(), | |
2212 request->GetTotalSentBytes()); | |
2213 EXPECT_EQ(network_delegate.total_network_bytes_received(), | |
2214 request->GetTotalReceivedBytes()); | |
2215 EXPECT_EQ(response_headers.data_len, request->raw_header_size()); | |
2216 EXPECT_TRUE(data.AllReadDataConsumed()); | |
2217 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
2218 } | |
2219 | |
2220 TEST_F(SpdyNetworkTransactionTest, | |
2221 TestRawHeaderSizeSuccessfullPushHeadersFirst) { | |
2222 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
2223 headers["user-agent"] = ""; | |
2224 headers["accept-encoding"] = "gzip, deflate"; | |
2225 | |
2226 SpdySerializedFrame req( | |
2227 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
2228 SpdySerializedFrame priority( | |
2229 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
2230 MockWrite writes[] = { | |
2231 CreateMockWrite(req, 0), CreateMockWrite(priority, 2), | |
2232 }; | |
2233 | |
2234 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2235 SpdySerializedFrame response_body_frame( | |
2236 spdy_util_.ConstructSpdyDataFrame(1, "should not include", 18, true)); | |
2237 | |
2238 SpdyHeaderBlock push_headers; | |
2239 spdy_util_.AddUrlToHeaderBlock(SpdyString(kDefaultUrl) + "b.dat", | |
2240 &push_headers); | |
2241 | |
2242 SpdySerializedFrame push_init_frame( | |
2243 spdy_util_.ConstructInitialSpdyPushFrame(std::move(push_headers), 2, 1)); | |
2244 | |
2245 SpdySerializedFrame push_headers_frame( | |
2246 spdy_util_.ConstructSpdyPushHeaders(2, nullptr, 0)); | |
2247 | |
2248 SpdySerializedFrame push_body_frame(spdy_util_.ConstructSpdyDataFrame( | |
2249 2, "should not include either", 25, false)); | |
2250 | |
2251 MockRead push_init_read(CreateMockRead(push_init_frame, 1)); | |
2252 MockRead response_headers(CreateMockRead(resp, 5)); | |
2253 // raw_header_size() will contain the size of the push promise frame | |
2254 // initialization. | |
2255 int expected_response_headers_size = | |
2256 response_headers.data_len + push_init_read.data_len; | |
2257 | |
2258 MockRead reads[] = { | |
2259 push_init_read, | |
2260 CreateMockRead(push_headers_frame, 3), | |
2261 CreateMockRead(push_body_frame, 4), | |
2262 response_headers, | |
2263 CreateMockRead(response_body_frame, 6), | |
2264 MockRead(ASYNC, 0, 7) // EOF | |
2265 }; | |
2266 | |
2267 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2268 | |
2269 TestDelegate delegate; | |
2270 SpdyURLRequestContext spdy_url_request_context; | |
2271 TestNetworkDelegate network_delegate; | |
2272 spdy_url_request_context.set_network_delegate(&network_delegate); | |
2273 SSLSocketDataProvider ssl_data(ASYNC, OK); | |
2274 ssl_data.next_proto = kProtoHTTP2; | |
2275 | |
2276 std::unique_ptr<URLRequest> request(spdy_url_request_context.CreateRequest( | |
2277 GURL(kDefaultUrl), DEFAULT_PRIORITY, &delegate)); | |
2278 spdy_url_request_context.socket_factory().AddSSLSocketDataProvider(&ssl_data); | |
2279 spdy_url_request_context.socket_factory().AddSocketDataProvider(&data); | |
2280 | |
2281 request->Start(); | |
2282 base::RunLoop().Run(); | |
2283 | |
2284 EXPECT_LT(0, request->GetTotalSentBytes()); | |
2285 EXPECT_LT(0, request->GetTotalReceivedBytes()); | |
2286 EXPECT_EQ(network_delegate.total_network_bytes_sent(), | |
2287 request->GetTotalSentBytes()); | |
2288 EXPECT_EQ(network_delegate.total_network_bytes_received(), | |
2289 request->GetTotalReceivedBytes()); | |
2290 EXPECT_EQ(expected_response_headers_size, request->raw_header_size()); | |
2291 EXPECT_TRUE(data.AllReadDataConsumed()); | |
2292 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
2293 } | |
2294 | |
2295 // Send a spdy request to www.example.org that gets redirected to www.foo.com. | |
2296 TEST_F(SpdyNetworkTransactionTest, DISABLED_RedirectGetRequest) { | |
2297 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
2298 headers["user-agent"] = ""; | |
2299 headers["accept-encoding"] = "gzip, deflate"; | |
2300 | |
2301 // Setup writes/reads to www.example.org | |
2302 SpdySerializedFrame req( | |
2303 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
2304 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReplyRedirect(1)); | |
2305 MockWrite writes[] = { | |
2306 CreateMockWrite(req, 1), | |
2307 }; | |
2308 MockRead reads[] = { | |
2309 CreateMockRead(resp, 2), MockRead(ASYNC, 0, 0, 3) // EOF | |
2310 }; | |
2311 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2312 | |
2313 // Setup writes/reads to www.foo.com | |
2314 SpdyHeaderBlock headers2( | |
2315 spdy_util_.ConstructGetHeaderBlock("http://www.foo.com/index.php")); | |
2316 headers2["user-agent"] = ""; | |
2317 headers2["accept-encoding"] = "gzip, deflate"; | |
2318 SpdySerializedFrame req2( | |
2319 spdy_util_.ConstructSpdyHeaders(1, std::move(headers2), LOWEST, true)); | |
2320 MockWrite writes2[] = { | |
2321 CreateMockWrite(req2, 1), | |
2322 }; | |
2323 | |
2324 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2325 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2326 MockRead reads2[] = { | |
2327 CreateMockRead(resp2, 2), CreateMockRead(body2, 3), | |
2328 MockRead(ASYNC, 0, 0, 4) // EOF | |
2329 }; | |
2330 | |
2331 SequencedSocketData data2(reads2, arraysize(reads2), writes2, | |
2332 arraysize(writes2)); | |
2333 | |
2334 // TODO(erikchen): Make test support SPDYSSL, SPDYNPN | |
2335 TestDelegate d; | |
2336 { | |
2337 SpdyURLRequestContext spdy_url_request_context; | |
2338 std::unique_ptr<URLRequest> r(spdy_url_request_context.CreateRequest( | |
2339 default_url_, DEFAULT_PRIORITY, &d)); | |
2340 spdy_url_request_context.socket_factory(). | |
2341 AddSocketDataProvider(&data); | |
2342 spdy_url_request_context.socket_factory(). | |
2343 AddSocketDataProvider(&data2); | |
2344 | |
2345 d.set_quit_on_redirect(true); | |
2346 r->Start(); | |
2347 base::RunLoop().Run(); | |
2348 | |
2349 EXPECT_EQ(1, d.received_redirect_count()); | |
2350 | |
2351 r->FollowDeferredRedirect(); | |
2352 base::RunLoop().Run(); | |
2353 EXPECT_EQ(1, d.response_started_count()); | |
2354 EXPECT_FALSE(d.received_data_before_response()); | |
2355 EXPECT_EQ(OK, d.request_status()); | |
2356 SpdyString contents("hello!"); | |
2357 EXPECT_EQ(contents, d.data_received()); | |
2358 } | |
2359 EXPECT_TRUE(data.AllReadDataConsumed()); | |
2360 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
2361 EXPECT_TRUE(data2.AllReadDataConsumed()); | |
2362 EXPECT_TRUE(data2.AllWriteDataConsumed()); | |
2363 } | |
2364 | |
2365 // Send a spdy request to www.example.org. Get a pushed stream that redirects to | |
2366 // www.foo.com. | |
2367 TEST_F(SpdyNetworkTransactionTest, DISABLED_RedirectServerPush) { | |
2368 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
2369 headers["user-agent"] = ""; | |
2370 headers["accept-encoding"] = "gzip, deflate"; | |
2371 | |
2372 // Setup writes/reads to www.example.org | |
2373 SpdySerializedFrame req( | |
2374 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
2375 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2376 SpdySerializedFrame rep(spdy_util_.ConstructSpdyPush( | |
2377 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str(), | |
2378 "301 Moved Permanently", "http://www.foo.com/index.php")); | |
2379 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2380 SpdySerializedFrame rst( | |
2381 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_CANCEL)); | |
2382 MockWrite writes[] = { | |
2383 CreateMockWrite(req, 1), CreateMockWrite(rst, 6), | |
2384 }; | |
2385 MockRead reads[] = { | |
2386 CreateMockRead(resp, 2), CreateMockRead(rep, 3), CreateMockRead(body, 4), | |
2387 MockRead(ASYNC, ERR_IO_PENDING, 5), // Force a pause | |
2388 MockRead(ASYNC, 0, 0, 7) // EOF | |
2389 }; | |
2390 | |
2391 // Setup writes/reads to www.foo.com | |
2392 SpdyHeaderBlock headers2( | |
2393 spdy_util_.ConstructGetHeaderBlock("http://www.foo.com/index.php")); | |
2394 headers2["user-agent"] = ""; | |
2395 headers2["accept-encoding"] = "gzip, deflate"; | |
2396 SpdySerializedFrame req2( | |
2397 spdy_util_.ConstructSpdyHeaders(1, std::move(headers2), LOWEST, true)); | |
2398 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2399 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2400 MockWrite writes2[] = { | |
2401 CreateMockWrite(req2, 1), | |
2402 }; | |
2403 MockRead reads2[] = { | |
2404 CreateMockRead(resp2, 2), CreateMockRead(body2, 3), | |
2405 MockRead(ASYNC, 0, 0, 5) // EOF | |
2406 }; | |
2407 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2408 SequencedSocketData data2(reads2, arraysize(reads2), writes2, | |
2409 arraysize(writes2)); | |
2410 | |
2411 TestDelegate d; | |
2412 TestDelegate d2; | |
2413 SpdyURLRequestContext spdy_url_request_context; | |
2414 { | |
2415 std::unique_ptr<URLRequest> r(spdy_url_request_context.CreateRequest( | |
2416 default_url_, DEFAULT_PRIORITY, &d)); | |
2417 spdy_url_request_context.socket_factory(). | |
2418 AddSocketDataProvider(&data); | |
2419 | |
2420 r->Start(); | |
2421 base::RunLoop().Run(); | |
2422 | |
2423 EXPECT_EQ(0, d.received_redirect_count()); | |
2424 SpdyString contents("hello!"); | |
2425 EXPECT_EQ(contents, d.data_received()); | |
2426 | |
2427 std::unique_ptr<URLRequest> r2(spdy_url_request_context.CreateRequest( | |
2428 GURL(GetDefaultUrlWithPath("/foo.dat")), DEFAULT_PRIORITY, &d2)); | |
2429 spdy_url_request_context.socket_factory(). | |
2430 AddSocketDataProvider(&data2); | |
2431 | |
2432 d2.set_quit_on_redirect(true); | |
2433 r2->Start(); | |
2434 base::RunLoop().Run(); | |
2435 EXPECT_EQ(1, d2.received_redirect_count()); | |
2436 | |
2437 r2->FollowDeferredRedirect(); | |
2438 base::RunLoop().Run(); | |
2439 EXPECT_EQ(1, d2.response_started_count()); | |
2440 EXPECT_FALSE(d2.received_data_before_response()); | |
2441 EXPECT_EQ(OK, d2.request_status()); | |
2442 SpdyString contents2("hello!"); | |
2443 EXPECT_EQ(contents2, d2.data_received()); | |
2444 } | |
2445 EXPECT_TRUE(data.AllReadDataConsumed()); | |
2446 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
2447 EXPECT_TRUE(data2.AllReadDataConsumed()); | |
2448 EXPECT_TRUE(data2.AllWriteDataConsumed()); | |
2449 } | |
2450 | |
2451 TEST_F(SpdyNetworkTransactionTest, ServerPushSingleDataFrame) { | |
2452 SpdySerializedFrame stream1_syn( | |
2453 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2454 SpdySerializedFrame stream2_priority( | |
2455 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
2456 MockWrite writes[] = { | |
2457 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
2458 }; | |
2459 | |
2460 SpdySerializedFrame stream1_reply( | |
2461 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2462 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
2463 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
2464 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2465 const char kPushedData[] = "pushed"; | |
2466 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
2467 2, kPushedData, strlen(kPushedData), true)); | |
2468 MockRead reads[] = { | |
2469 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
2470 CreateMockRead(stream1_body, 4), CreateMockRead(stream2_body, 5), | |
2471 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause | |
2472 }; | |
2473 | |
2474 HttpResponseInfo response; | |
2475 HttpResponseInfo response2; | |
2476 SpdyString expected_push_result("pushed"); | |
2477 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2478 RunServerPushTest(&data, | |
2479 &response, | |
2480 &response2, | |
2481 expected_push_result); | |
2482 | |
2483 // Verify the response headers. | |
2484 EXPECT_TRUE(response.headers); | |
2485 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
2486 | |
2487 // Verify the pushed stream. | |
2488 EXPECT_TRUE(response2.headers); | |
2489 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
2490 } | |
2491 | |
2492 TEST_F(SpdyNetworkTransactionTest, ServerPushBeforeHeaders) { | |
2493 SpdySerializedFrame stream1_syn( | |
2494 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2495 SpdySerializedFrame stream2_priority( | |
2496 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
2497 MockWrite writes[] = { | |
2498 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 2), | |
2499 }; | |
2500 | |
2501 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
2502 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
2503 SpdySerializedFrame stream1_reply( | |
2504 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2505 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2506 const char kPushedData[] = "pushed"; | |
2507 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
2508 2, kPushedData, strlen(kPushedData), true)); | |
2509 MockRead reads[] = { | |
2510 CreateMockRead(stream2_syn, 1), | |
2511 CreateMockRead(stream1_reply, 3), | |
2512 CreateMockRead(stream1_body, 4, SYNCHRONOUS), | |
2513 CreateMockRead(stream2_body, 5), | |
2514 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause | |
2515 }; | |
2516 | |
2517 HttpResponseInfo response; | |
2518 HttpResponseInfo response2; | |
2519 SpdyString expected_push_result("pushed"); | |
2520 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2521 RunServerPushTest(&data, | |
2522 &response, | |
2523 &response2, | |
2524 expected_push_result); | |
2525 | |
2526 // Verify the response headers. | |
2527 EXPECT_TRUE(response.headers); | |
2528 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
2529 | |
2530 // Verify the pushed stream. | |
2531 EXPECT_TRUE(response2.headers); | |
2532 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
2533 } | |
2534 | |
2535 TEST_F(SpdyNetworkTransactionTest, ServerPushSingleDataFrame2) { | |
2536 SpdySerializedFrame stream1_syn( | |
2537 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2538 SpdySerializedFrame stream2_priority( | |
2539 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
2540 MockWrite writes[] = { | |
2541 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
2542 }; | |
2543 | |
2544 SpdySerializedFrame stream1_reply( | |
2545 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2546 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
2547 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
2548 const char kPushedData[] = "pushed"; | |
2549 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
2550 2, kPushedData, strlen(kPushedData), true)); | |
2551 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2552 MockRead reads[] = { | |
2553 CreateMockRead(stream1_reply, 1), | |
2554 CreateMockRead(stream2_syn, 2), | |
2555 CreateMockRead(stream2_body, 4), | |
2556 CreateMockRead(stream1_body, 5, SYNCHRONOUS), | |
2557 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause | |
2558 }; | |
2559 | |
2560 HttpResponseInfo response; | |
2561 HttpResponseInfo response2; | |
2562 SpdyString expected_push_result("pushed"); | |
2563 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2564 RunServerPushTest(&data, | |
2565 &response, | |
2566 &response2, | |
2567 expected_push_result); | |
2568 | |
2569 // Verify the response headers. | |
2570 EXPECT_TRUE(response.headers); | |
2571 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
2572 | |
2573 // Verify the pushed stream. | |
2574 EXPECT_TRUE(response2.headers); | |
2575 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
2576 } | |
2577 | |
2578 TEST_F(SpdyNetworkTransactionTest, ServerPushUpdatesPriority) { | |
2579 SpdySerializedFrame stream1_headers( | |
2580 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST, true)); | |
2581 SpdySerializedFrame stream3_headers( | |
2582 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, MEDIUM, true)); | |
2583 SpdySerializedFrame stream5_headers( | |
2584 spdy_util_.ConstructSpdyGet(nullptr, 0, 5, MEDIUM, true)); | |
2585 | |
2586 // Stream 1 pushes two streams that are initially prioritized below stream 5. | |
2587 // Stream 2 is later prioritized below stream 1 after it matches a request. | |
2588 SpdySerializedFrame stream2_priority( | |
2589 spdy_util_.ConstructSpdyPriority(2, 5, IDLE, true)); | |
2590 SpdySerializedFrame stream4_priority( | |
2591 spdy_util_.ConstructSpdyPriority(4, 2, IDLE, true)); | |
2592 SpdySerializedFrame stream4_priority_update( | |
2593 spdy_util_.ConstructSpdyPriority(4, 5, IDLE, true)); | |
2594 SpdySerializedFrame stream2_priority_update( | |
2595 spdy_util_.ConstructSpdyPriority(2, 1, HIGHEST, true)); | |
2596 | |
2597 MockWrite writes[] = { | |
2598 CreateMockWrite(stream1_headers, 0), | |
2599 CreateMockWrite(stream3_headers, 1), | |
2600 CreateMockWrite(stream5_headers, 2), | |
2601 CreateMockWrite(stream2_priority, 7), | |
2602 CreateMockWrite(stream4_priority, 9), | |
2603 CreateMockWrite(stream4_priority_update, 11), | |
2604 CreateMockWrite(stream2_priority_update, 12), | |
2605 }; | |
2606 | |
2607 SpdySerializedFrame stream1_reply( | |
2608 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2609 SpdySerializedFrame stream3_reply( | |
2610 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
2611 SpdySerializedFrame stream5_reply( | |
2612 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5)); | |
2613 | |
2614 SpdySerializedFrame stream2_push(spdy_util_.ConstructSpdyPush( | |
2615 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
2616 SpdySerializedFrame stream4_push(spdy_util_.ConstructSpdyPush( | |
2617 nullptr, 0, 4, 1, GetDefaultUrlWithPath("/bar.dat").c_str())); | |
2618 | |
2619 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2620 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame(2, true)); | |
2621 SpdySerializedFrame stream3_body(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
2622 SpdySerializedFrame stream5_body(spdy_util_.ConstructSpdyDataFrame(5, true)); | |
2623 | |
2624 MockRead reads[] = { | |
2625 CreateMockRead(stream1_reply, 3), | |
2626 CreateMockRead(stream3_reply, 4), | |
2627 CreateMockRead(stream5_reply, 5), | |
2628 CreateMockRead(stream2_push, 6), | |
2629 CreateMockRead(stream4_push, 8), | |
2630 MockRead(ASYNC, ERR_IO_PENDING, 10), | |
2631 CreateMockRead(stream1_body, 13), | |
2632 CreateMockRead(stream2_body, 14), | |
2633 CreateMockRead(stream3_body, 15), | |
2634 CreateMockRead(stream5_body, 16), | |
2635 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 17), // Force a pause | |
2636 }; | |
2637 | |
2638 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2639 SequencedSocketData data_placeholder1(nullptr, 0, nullptr, 0); | |
2640 SequencedSocketData data_placeholder2(nullptr, 0, nullptr, 0); | |
2641 SequencedSocketData data_placeholder3(nullptr, 0, nullptr, 0); | |
2642 | |
2643 NormalSpdyTransactionHelper helper(CreateGetRequest(), LOWEST, | |
2644 NetLogWithSource(), nullptr); | |
2645 helper.RunPreTestSetup(); | |
2646 helper.AddData(&data); | |
2647 helper.AddData(&data_placeholder1); // other requests reuse the same socket | |
2648 helper.AddData(&data_placeholder2); | |
2649 helper.AddData(&data_placeholder3); | |
2650 HttpNetworkTransaction trans1(HIGHEST, helper.session()); | |
2651 HttpNetworkTransaction trans3(MEDIUM, helper.session()); | |
2652 HttpNetworkTransaction trans5(MEDIUM, helper.session()); | |
2653 | |
2654 TestCompletionCallback callback1; | |
2655 TestCompletionCallback callback3; | |
2656 TestCompletionCallback callback5; | |
2657 | |
2658 // Start the ordinary requests. | |
2659 NetLogWithSource log; | |
2660 ASSERT_THAT(trans1.Start(&CreateGetRequest(), callback1.callback(), log), | |
2661 IsError(ERR_IO_PENDING)); | |
2662 ASSERT_THAT(trans3.Start(&CreateGetRequest(), callback3.callback(), log), | |
2663 IsError(ERR_IO_PENDING)); | |
2664 ASSERT_THAT(trans5.Start(&CreateGetRequest(), callback5.callback(), log), | |
2665 IsError(ERR_IO_PENDING)); | |
2666 data.RunUntilPaused(); | |
2667 | |
2668 // Start a request that matches the push. | |
2669 HttpRequestInfo push_req = CreateGetRequest(); | |
2670 push_req.url = GURL(GetDefaultUrlWithPath("/foo.dat")); | |
2671 | |
2672 HttpNetworkTransaction trans2(HIGHEST, helper.session()); | |
2673 TestCompletionCallback callback2; | |
2674 ASSERT_THAT(trans2.Start(&push_req, callback2.callback(), log), | |
2675 IsError(ERR_IO_PENDING)); | |
2676 data.Resume(); | |
2677 | |
2678 base::RunLoop().RunUntilIdle(); | |
2679 ASSERT_THAT(callback1.WaitForResult(), IsOk()); | |
2680 ASSERT_THAT(callback2.WaitForResult(), IsOk()); | |
2681 ASSERT_THAT(callback3.WaitForResult(), IsOk()); | |
2682 ASSERT_THAT(callback5.WaitForResult(), IsOk()); | |
2683 helper.VerifyDataConsumed(); | |
2684 } | |
2685 | |
2686 TEST_F(SpdyNetworkTransactionTest, ServerPushServerAborted) { | |
2687 SpdySerializedFrame stream1_syn( | |
2688 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2689 SpdySerializedFrame stream2_priority( | |
2690 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
2691 MockWrite writes[] = { | |
2692 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
2693 }; | |
2694 | |
2695 SpdySerializedFrame stream1_reply( | |
2696 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2697 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
2698 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
2699 SpdySerializedFrame stream2_rst( | |
2700 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_PROTOCOL_ERROR)); | |
2701 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2702 MockRead reads[] = { | |
2703 CreateMockRead(stream1_reply, 1), | |
2704 CreateMockRead(stream2_syn, 2, SYNCHRONOUS), | |
2705 CreateMockRead(stream2_rst, 4), | |
2706 CreateMockRead(stream1_body, 5, SYNCHRONOUS), | |
2707 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause | |
2708 }; | |
2709 | |
2710 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2711 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
2712 NetLogWithSource(), nullptr); | |
2713 | |
2714 helper.RunPreTestSetup(); | |
2715 helper.AddData(&data); | |
2716 | |
2717 HttpNetworkTransaction* trans = helper.trans(); | |
2718 | |
2719 // Start the transaction with basic parameters. | |
2720 TestCompletionCallback callback; | |
2721 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
2722 NetLogWithSource()); | |
2723 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
2724 rv = callback.WaitForResult(); | |
2725 EXPECT_THAT(rv, IsOk()); | |
2726 | |
2727 // Verify that we consumed all test data. | |
2728 base::RunLoop().RunUntilIdle(); | |
2729 EXPECT_TRUE(data.AllReadDataConsumed()); | |
2730 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
2731 | |
2732 // Verify the response headers. | |
2733 HttpResponseInfo response = *trans->GetResponseInfo(); | |
2734 EXPECT_TRUE(response.headers); | |
2735 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
2736 } | |
2737 | |
2738 // Verify that we don't leak streams and that we properly send a reset | |
2739 // if the server pushes the same stream twice. | |
2740 TEST_F(SpdyNetworkTransactionTest, ServerPushDuplicate) { | |
2741 SpdySerializedFrame stream1_syn( | |
2742 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2743 SpdySerializedFrame stream2_priority( | |
2744 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
2745 SpdySerializedFrame stream3_rst( | |
2746 spdy_util_.ConstructSpdyRstStream(4, ERROR_CODE_PROTOCOL_ERROR)); | |
2747 MockWrite writes[] = { | |
2748 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
2749 CreateMockWrite(stream3_rst, 5), | |
2750 }; | |
2751 | |
2752 SpdySerializedFrame stream1_reply( | |
2753 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2754 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
2755 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
2756 SpdySerializedFrame stream3_syn(spdy_util_.ConstructSpdyPush( | |
2757 nullptr, 0, 4, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
2758 | |
2759 const char kPushedData[] = "pushed"; | |
2760 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2761 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
2762 2, kPushedData, strlen(kPushedData), true)); | |
2763 | |
2764 MockRead reads[] = { | |
2765 CreateMockRead(stream1_reply, 1), | |
2766 CreateMockRead(stream2_syn, 2), | |
2767 CreateMockRead(stream3_syn, 4), | |
2768 CreateMockRead(stream1_body, 6), | |
2769 CreateMockRead(stream2_body, 7), | |
2770 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 8), // Force a pause | |
2771 }; | |
2772 | |
2773 HttpResponseInfo response; | |
2774 HttpResponseInfo response2; | |
2775 SpdyString expected_push_result("pushed"); | |
2776 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2777 RunServerPushTest(&data, | |
2778 &response, | |
2779 &response2, | |
2780 expected_push_result); | |
2781 | |
2782 // Verify the response headers. | |
2783 EXPECT_TRUE(response.headers); | |
2784 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
2785 | |
2786 // Verify the pushed stream. | |
2787 EXPECT_TRUE(response2.headers); | |
2788 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
2789 } | |
2790 | |
2791 TEST_F(SpdyNetworkTransactionTest, ServerPushMultipleDataFrame) { | |
2792 SpdySerializedFrame stream1_syn( | |
2793 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2794 SpdySerializedFrame stream2_priority( | |
2795 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
2796 MockWrite writes[] = { | |
2797 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
2798 }; | |
2799 | |
2800 SpdySerializedFrame stream1_reply( | |
2801 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2802 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
2803 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
2804 static const char kPushedData[] = "pushed my darling hello my baby"; | |
2805 SpdySerializedFrame stream2_body_base(spdy_util_.ConstructSpdyDataFrame( | |
2806 2, kPushedData, strlen(kPushedData), true)); | |
2807 const size_t kChunkSize = strlen(kPushedData) / 4; | |
2808 SpdySerializedFrame stream2_body1(stream2_body_base.data(), kChunkSize, | |
2809 false); | |
2810 SpdySerializedFrame stream2_body2(stream2_body_base.data() + kChunkSize, | |
2811 kChunkSize, false); | |
2812 SpdySerializedFrame stream2_body3(stream2_body_base.data() + 2 * kChunkSize, | |
2813 kChunkSize, false); | |
2814 SpdySerializedFrame stream2_body4(stream2_body_base.data() + 3 * kChunkSize, | |
2815 stream2_body_base.size() - 3 * kChunkSize, | |
2816 false); | |
2817 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2818 MockRead reads[] = { | |
2819 CreateMockRead(stream1_reply, 1), | |
2820 CreateMockRead(stream2_syn, 2), | |
2821 CreateMockRead(stream2_body1, 4), | |
2822 CreateMockRead(stream2_body2, 5), | |
2823 CreateMockRead(stream2_body3, 6), | |
2824 CreateMockRead(stream2_body4, 7), | |
2825 CreateMockRead(stream1_body, 8, SYNCHRONOUS), | |
2826 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 9), // Force a pause | |
2827 }; | |
2828 | |
2829 HttpResponseInfo response; | |
2830 HttpResponseInfo response2; | |
2831 SpdyString expected_push_result("pushed my darling hello my baby"); | |
2832 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2833 RunServerPushTest(&data, &response, &response2, kPushedData); | |
2834 | |
2835 // Verify the response headers. | |
2836 EXPECT_TRUE(response.headers); | |
2837 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
2838 | |
2839 // Verify the pushed stream. | |
2840 EXPECT_TRUE(response2.headers); | |
2841 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
2842 } | |
2843 | |
2844 TEST_F(SpdyNetworkTransactionTest, ServerPushMultipleDataFrameInterrupted) { | |
2845 SpdySerializedFrame stream1_syn( | |
2846 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2847 SpdySerializedFrame stream2_priority( | |
2848 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
2849 MockWrite writes[] = { | |
2850 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
2851 }; | |
2852 | |
2853 SpdySerializedFrame stream1_reply( | |
2854 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2855 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
2856 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
2857 static const char kPushedData[] = "pushed my darling hello my baby"; | |
2858 SpdySerializedFrame stream2_body_base(spdy_util_.ConstructSpdyDataFrame( | |
2859 2, kPushedData, strlen(kPushedData), true)); | |
2860 const size_t kChunkSize = strlen(kPushedData) / 4; | |
2861 SpdySerializedFrame stream2_body1(stream2_body_base.data(), kChunkSize, | |
2862 false); | |
2863 SpdySerializedFrame stream2_body2(stream2_body_base.data() + kChunkSize, | |
2864 kChunkSize, false); | |
2865 SpdySerializedFrame stream2_body3(stream2_body_base.data() + 2 * kChunkSize, | |
2866 kChunkSize, false); | |
2867 SpdySerializedFrame stream2_body4(stream2_body_base.data() + 3 * kChunkSize, | |
2868 stream2_body_base.size() - 3 * kChunkSize, | |
2869 false); | |
2870 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2871 MockRead reads[] = { | |
2872 CreateMockRead(stream1_reply, 1), | |
2873 CreateMockRead(stream2_syn, 2), | |
2874 CreateMockRead(stream2_body1, 4), | |
2875 CreateMockRead(stream2_body2, 5), | |
2876 CreateMockRead(stream2_body3, 6), | |
2877 CreateMockRead(stream2_body4, 7), | |
2878 CreateMockRead(stream1_body, 8, SYNCHRONOUS), | |
2879 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 9) // Force a pause. | |
2880 }; | |
2881 | |
2882 HttpResponseInfo response; | |
2883 HttpResponseInfo response2; | |
2884 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2885 RunServerPushTest(&data, &response, &response2, kPushedData); | |
2886 | |
2887 // Verify the response headers. | |
2888 EXPECT_TRUE(response.headers); | |
2889 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
2890 | |
2891 // Verify the pushed stream. | |
2892 EXPECT_TRUE(response2.headers); | |
2893 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
2894 } | |
2895 | |
2896 TEST_F(SpdyNetworkTransactionTest, ServerPushInvalidUrl) { | |
2897 // Coverage on how a non-empty invalid GURL in a PUSH_PROMISE is handled. | |
2898 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
2899 SpdySerializedFrame req( | |
2900 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
2901 | |
2902 // Can't use ConstructSpdyPush here since it wants to parse a URL and | |
2903 // split it into the appropriate :header pieces. So we have to hand-fill | |
2904 // those pieces in. | |
2905 SpdyFramer response_spdy_framer(SpdyFramer::ENABLE_COMPRESSION); | |
2906 SpdyHeaderBlock push_promise_header_block; | |
2907 push_promise_header_block[spdy_util_.GetHostKey()] = ""; | |
2908 push_promise_header_block[spdy_util_.GetSchemeKey()] = ""; | |
2909 push_promise_header_block[spdy_util_.GetPathKey()] = "/index.html"; | |
2910 | |
2911 SpdyPushPromiseIR push_promise(1, 2, std::move(push_promise_header_block)); | |
2912 SpdySerializedFrame push_promise_frame( | |
2913 response_spdy_framer.SerializeFrame(push_promise)); | |
2914 | |
2915 SpdySerializedFrame stream2_rst( | |
2916 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_PROTOCOL_ERROR)); | |
2917 | |
2918 MockWrite writes[] = {CreateMockWrite(req, 0), | |
2919 CreateMockWrite(stream2_rst, 2)}; | |
2920 MockRead reads[] = { | |
2921 CreateMockRead(push_promise_frame, 1), MockRead(ASYNC, 0, 3) /* EOF */ | |
2922 }; | |
2923 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2924 RunBrokenPushTest(&data, ERR_CONNECTION_CLOSED); | |
2925 } | |
2926 | |
2927 TEST_F(SpdyNetworkTransactionTest, ServerPushInvalidAssociatedStreamID0) { | |
2928 SpdySerializedFrame stream1_syn( | |
2929 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2930 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
2931 0, ERROR_CODE_PROTOCOL_ERROR, "Framer error: 1 (INVALID_STREAM_ID).")); | |
2932 MockWrite writes[] = { | |
2933 CreateMockWrite(stream1_syn, 0), CreateMockWrite(goaway, 3), | |
2934 }; | |
2935 | |
2936 SpdySerializedFrame stream1_reply( | |
2937 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2938 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
2939 nullptr, 0, 2, 0, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
2940 MockRead reads[] = { | |
2941 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
2942 }; | |
2943 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2944 RunBrokenPushTest(&data, OK); | |
2945 } | |
2946 | |
2947 TEST_F(SpdyNetworkTransactionTest, ServerPushInvalidAssociatedStreamID9) { | |
2948 SpdySerializedFrame stream1_syn( | |
2949 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2950 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2951 SpdySerializedFrame stream2_rst( | |
2952 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_STREAM_CLOSED)); | |
2953 MockWrite writes[] = { | |
2954 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_rst, 3), | |
2955 }; | |
2956 | |
2957 SpdySerializedFrame stream1_reply( | |
2958 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2959 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
2960 nullptr, 0, 2, 9, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
2961 MockRead reads[] = { | |
2962 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
2963 CreateMockRead(stream1_body, 4), | |
2964 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5), // Force a pause | |
2965 }; | |
2966 | |
2967 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2968 RunBrokenPushTest(&data, OK); | |
2969 } | |
2970 | |
2971 TEST_F(SpdyNetworkTransactionTest, ServerPushNoURL) { | |
2972 SpdySerializedFrame stream1_syn( | |
2973 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
2974 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
2975 SpdySerializedFrame stream2_rst( | |
2976 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_PROTOCOL_ERROR)); | |
2977 MockWrite writes[] = { | |
2978 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_rst, 3), | |
2979 }; | |
2980 | |
2981 SpdySerializedFrame stream1_reply( | |
2982 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
2983 SpdyHeaderBlock incomplete_headers; | |
2984 incomplete_headers[spdy_util_.GetStatusKey()] = "200 OK"; | |
2985 incomplete_headers["hello"] = "bye"; | |
2986 SpdySerializedFrame stream2_syn(spdy_util_.ConstructInitialSpdyPushFrame( | |
2987 std::move(incomplete_headers), 2, 1)); | |
2988 MockRead reads[] = { | |
2989 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
2990 CreateMockRead(stream1_body, 4), | |
2991 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5) // Force a pause | |
2992 }; | |
2993 | |
2994 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
2995 RunBrokenPushTest(&data, OK); | |
2996 } | |
2997 | |
2998 // PUSH_PROMISE on a server-initiated stream should trigger GOAWAY. | |
2999 TEST_F(SpdyNetworkTransactionTest, ServerPushOnPushedStream) { | |
3000 SpdySerializedFrame stream1_syn( | |
3001 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3002 SpdySerializedFrame stream2_priority( | |
3003 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
3004 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
3005 2, ERROR_CODE_PROTOCOL_ERROR, "Push on even stream id.")); | |
3006 MockWrite writes[] = { | |
3007 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
3008 CreateMockWrite(goaway, 5), | |
3009 }; | |
3010 | |
3011 SpdySerializedFrame stream1_reply( | |
3012 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3013 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
3014 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
3015 SpdySerializedFrame stream3_syn(spdy_util_.ConstructSpdyPush( | |
3016 nullptr, 0, 4, 2, GetDefaultUrlWithPath("/bar.dat").c_str())); | |
3017 MockRead reads[] = { | |
3018 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
3019 CreateMockRead(stream3_syn, 4), | |
3020 }; | |
3021 | |
3022 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3023 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3024 NetLogWithSource(), nullptr); | |
3025 helper.RunToCompletion(&data); | |
3026 } | |
3027 | |
3028 // PUSH_PROMISE on a closed client-initiated stream should trigger RST_STREAM. | |
3029 TEST_F(SpdyNetworkTransactionTest, ServerPushOnClosedStream) { | |
3030 SpdySerializedFrame stream1_syn( | |
3031 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3032 SpdySerializedFrame rst( | |
3033 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_STREAM_CLOSED)); | |
3034 MockWrite writes[] = { | |
3035 CreateMockWrite(stream1_syn, 0), CreateMockWrite(rst, 5), | |
3036 }; | |
3037 | |
3038 SpdySerializedFrame stream1_reply( | |
3039 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3040 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
3041 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
3042 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
3043 MockRead reads[] = { | |
3044 CreateMockRead(stream1_reply, 1), CreateMockRead(stream1_body, 2), | |
3045 CreateMockRead(stream2_syn, 3), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 4), | |
3046 }; | |
3047 | |
3048 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3049 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3050 NetLogWithSource(), nullptr); | |
3051 helper.RunPreTestSetup(); | |
3052 helper.AddData(&data); | |
3053 | |
3054 HttpNetworkTransaction* trans = helper.trans(); | |
3055 | |
3056 TestCompletionCallback callback; | |
3057 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
3058 NetLogWithSource()); | |
3059 rv = callback.GetResult(rv); | |
3060 EXPECT_THAT(rv, IsOk()); | |
3061 | |
3062 // Finish async network reads/writes. | |
3063 base::RunLoop().RunUntilIdle(); | |
3064 | |
3065 HttpResponseInfo response = *trans->GetResponseInfo(); | |
3066 EXPECT_TRUE(response.headers); | |
3067 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
3068 | |
3069 EXPECT_TRUE(data.AllReadDataConsumed()); | |
3070 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
3071 VerifyStreamsClosed(helper); | |
3072 } | |
3073 | |
3074 // PUSH_PROMISE on a server-initiated stream should trigger GOAWAY even if | |
3075 // stream is closed. | |
3076 TEST_F(SpdyNetworkTransactionTest, ServerPushOnClosedPushedStream) { | |
3077 SpdySerializedFrame stream1_syn( | |
3078 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3079 SpdySerializedFrame stream2_priority( | |
3080 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
3081 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
3082 2, ERROR_CODE_PROTOCOL_ERROR, "Push on even stream id.")); | |
3083 MockWrite writes[] = { | |
3084 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
3085 CreateMockWrite(goaway, 8), | |
3086 }; | |
3087 | |
3088 SpdySerializedFrame stream1_reply( | |
3089 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3090 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
3091 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
3092 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
3093 const char kPushedData[] = "pushed"; | |
3094 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
3095 2, kPushedData, strlen(kPushedData), true)); | |
3096 SpdySerializedFrame stream3_syn(spdy_util_.ConstructSpdyPush( | |
3097 nullptr, 0, 4, 2, GetDefaultUrlWithPath("/bar.dat").c_str())); | |
3098 | |
3099 MockRead reads[] = { | |
3100 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
3101 CreateMockRead(stream1_body, 4), CreateMockRead(stream2_body, 5), | |
3102 MockRead(ASYNC, ERR_IO_PENDING, 6), CreateMockRead(stream3_syn, 7), | |
3103 }; | |
3104 | |
3105 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3106 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3107 NetLogWithSource(), nullptr); | |
3108 helper.RunPreTestSetup(); | |
3109 helper.AddData(&data); | |
3110 | |
3111 HttpNetworkTransaction* trans1 = helper.trans(); | |
3112 TestCompletionCallback callback1; | |
3113 int rv = trans1->Start(&CreateGetRequest(), callback1.callback(), | |
3114 NetLogWithSource()); | |
3115 rv = callback1.GetResult(rv); | |
3116 EXPECT_THAT(rv, IsOk()); | |
3117 HttpResponseInfo response = *trans1->GetResponseInfo(); | |
3118 EXPECT_TRUE(response.headers); | |
3119 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
3120 | |
3121 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
3122 TestCompletionCallback callback2; | |
3123 rv = trans2.Start(&CreateGetPushRequest(), callback2.callback(), | |
3124 NetLogWithSource()); | |
3125 rv = callback2.GetResult(rv); | |
3126 EXPECT_THAT(rv, IsOk()); | |
3127 response = *trans2.GetResponseInfo(); | |
3128 EXPECT_TRUE(response.headers); | |
3129 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
3130 SpdyString result; | |
3131 ReadResult(&trans2, &result); | |
3132 EXPECT_EQ(kPushedData, result); | |
3133 | |
3134 data.Resume(); | |
3135 base::RunLoop().RunUntilIdle(); | |
3136 | |
3137 EXPECT_TRUE(data.AllReadDataConsumed()); | |
3138 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
3139 } | |
3140 | |
3141 // Verify that various response headers parse correctly through the HTTP layer. | |
3142 TEST_F(SpdyNetworkTransactionTest, ResponseHeaders) { | |
3143 struct ResponseHeadersTests { | |
3144 int num_headers; | |
3145 const char* extra_headers[5]; | |
3146 SpdyHeaderBlock expected_headers; | |
3147 } test_cases[] = {// This uses a multi-valued cookie header. | |
3148 { | |
3149 2, | |
3150 {"cookie", "val1", "cookie", | |
3151 "val2", // will get appended separated by nullptr | |
3152 nullptr}, | |
3153 }, | |
3154 // This is the minimalist set of headers. | |
3155 { | |
3156 0, {nullptr}, | |
3157 }, | |
3158 // Headers with a comma separated list. | |
3159 { | |
3160 1, {"cookie", "val1,val2", nullptr}, | |
3161 }}; | |
3162 | |
3163 test_cases[0].expected_headers["status"] = "200"; | |
3164 test_cases[1].expected_headers["status"] = "200"; | |
3165 test_cases[2].expected_headers["status"] = "200"; | |
3166 | |
3167 test_cases[0].expected_headers["hello"] = "bye"; | |
3168 test_cases[1].expected_headers["hello"] = "bye"; | |
3169 test_cases[2].expected_headers["hello"] = "bye"; | |
3170 | |
3171 test_cases[0].expected_headers["cookie"] = SpdyStringPiece("val1\0val2", 9); | |
3172 test_cases[2].expected_headers["cookie"] = "val1,val2"; | |
3173 | |
3174 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
3175 SpdyTestUtil spdy_test_util; | |
3176 SpdySerializedFrame req( | |
3177 spdy_test_util.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3178 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
3179 | |
3180 SpdySerializedFrame resp(spdy_test_util.ConstructSpdyGetReply( | |
3181 test_cases[i].extra_headers, test_cases[i].num_headers, 1)); | |
3182 SpdySerializedFrame body(spdy_test_util.ConstructSpdyDataFrame(1, true)); | |
3183 MockRead reads[] = { | |
3184 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
3185 MockRead(ASYNC, 0, 3) // EOF | |
3186 }; | |
3187 | |
3188 SequencedSocketData data(reads, arraysize(reads), writes, | |
3189 arraysize(writes)); | |
3190 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3191 NetLogWithSource(), nullptr); | |
3192 helper.RunToCompletion(&data); | |
3193 TransactionHelperResult out = helper.output(); | |
3194 | |
3195 EXPECT_THAT(out.rv, IsOk()); | |
3196 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
3197 EXPECT_EQ("hello!", out.response_data); | |
3198 | |
3199 scoped_refptr<HttpResponseHeaders> headers = out.response_info.headers; | |
3200 EXPECT_TRUE(headers); | |
3201 size_t iter = 0; | |
3202 SpdyString name, value; | |
3203 SpdyHeaderBlock header_block; | |
3204 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
3205 auto value_it = header_block.find(name); | |
3206 if (value_it == header_block.end() || value_it->second.empty()) { | |
3207 header_block[name] = value; | |
3208 } else { | |
3209 SpdyString joint_value = value_it->second.as_string(); | |
3210 joint_value.append(1, '\0'); | |
3211 joint_value.append(value); | |
3212 header_block[name] = joint_value; | |
3213 } | |
3214 } | |
3215 EXPECT_EQ(test_cases[i].expected_headers, header_block); | |
3216 } | |
3217 } | |
3218 | |
3219 // Verify that various response headers parse vary fields correctly through the | |
3220 // HTTP layer, and the response matches the request. | |
3221 TEST_F(SpdyNetworkTransactionTest, ResponseHeadersVary) { | |
3222 // Modify the following data to change/add test cases: | |
3223 struct ResponseTests { | |
3224 bool vary_matches; | |
3225 int num_headers[2]; | |
3226 const char* extra_headers[2][16]; | |
3227 } test_cases[] = { | |
3228 // Test the case of a multi-valued cookie. When the value is delimited | |
3229 // with NUL characters, it needs to be unfolded into multiple headers. | |
3230 {true, | |
3231 {1, 3}, | |
3232 {{"cookie", "val1,val2", nullptr}, | |
3233 {spdy_util_.GetStatusKey(), "200", spdy_util_.GetPathKey(), | |
3234 "/index.php", "vary", "cookie", nullptr}}}, | |
3235 {// Multiple vary fields. | |
3236 true, | |
3237 {2, 4}, | |
3238 {{"friend", "barney", "enemy", "snaggletooth", nullptr}, | |
3239 {spdy_util_.GetStatusKey(), "200", spdy_util_.GetPathKey(), | |
3240 "/index.php", "vary", "friend", "vary", "enemy", nullptr}}}, | |
3241 {// Test a '*' vary field. | |
3242 false, | |
3243 {1, 3}, | |
3244 {{"cookie", "val1,val2", nullptr}, | |
3245 {spdy_util_.GetStatusKey(), "200", spdy_util_.GetPathKey(), | |
3246 "/index.php", "vary", "*", nullptr}}}, | |
3247 {// Multiple comma-separated vary fields. | |
3248 true, | |
3249 {2, 3}, | |
3250 {{"friend", "barney", "enemy", "snaggletooth", nullptr}, | |
3251 {spdy_util_.GetStatusKey(), "200", spdy_util_.GetPathKey(), | |
3252 "/index.php", "vary", "friend,enemy", nullptr}}}}; | |
3253 | |
3254 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
3255 SpdyTestUtil spdy_test_util; | |
3256 | |
3257 // Construct the request. | |
3258 SpdySerializedFrame frame_req(spdy_test_util.ConstructSpdyGet( | |
3259 test_cases[i].extra_headers[0], test_cases[i].num_headers[0], 1, LOWEST, | |
3260 true)); | |
3261 | |
3262 MockWrite writes[] = { | |
3263 CreateMockWrite(frame_req, 0), | |
3264 }; | |
3265 | |
3266 // Construct the reply. | |
3267 SpdyHeaderBlock reply_headers; | |
3268 AppendToHeaderBlock(test_cases[i].extra_headers[1], | |
3269 test_cases[i].num_headers[1], | |
3270 &reply_headers); | |
3271 // Construct the expected header reply string before moving |reply_headers|. | |
3272 SpdyString expected_reply = | |
3273 spdy_test_util.ConstructSpdyReplyString(reply_headers); | |
3274 | |
3275 SpdySerializedFrame frame_reply( | |
3276 spdy_test_util.ConstructSpdyReply(1, std::move(reply_headers))); | |
3277 | |
3278 SpdySerializedFrame body(spdy_test_util.ConstructSpdyDataFrame(1, true)); | |
3279 MockRead reads[] = { | |
3280 CreateMockRead(frame_reply, 1), CreateMockRead(body, 2), | |
3281 MockRead(ASYNC, 0, 3) // EOF | |
3282 }; | |
3283 | |
3284 // Attach the headers to the request. | |
3285 int header_count = test_cases[i].num_headers[0]; | |
3286 | |
3287 HttpRequestInfo request = CreateGetRequest(); | |
3288 for (int ct = 0; ct < header_count; ct++) { | |
3289 const char* header_key = test_cases[i].extra_headers[0][ct * 2]; | |
3290 const char* header_value = test_cases[i].extra_headers[0][ct * 2 + 1]; | |
3291 request.extra_headers.SetHeader(header_key, header_value); | |
3292 } | |
3293 | |
3294 SequencedSocketData data(reads, arraysize(reads), writes, | |
3295 arraysize(writes)); | |
3296 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
3297 NetLogWithSource(), nullptr); | |
3298 helper.RunToCompletion(&data); | |
3299 TransactionHelperResult out = helper.output(); | |
3300 | |
3301 EXPECT_EQ(OK, out.rv) << i; | |
3302 EXPECT_EQ("HTTP/1.1 200", out.status_line) << i; | |
3303 EXPECT_EQ("hello!", out.response_data) << i; | |
3304 | |
3305 // Test the response information. | |
3306 EXPECT_EQ(out.response_info.vary_data.is_valid(), | |
3307 test_cases[i].vary_matches) << i; | |
3308 | |
3309 // Check the headers. | |
3310 scoped_refptr<HttpResponseHeaders> headers = out.response_info.headers; | |
3311 ASSERT_TRUE(headers) << i; | |
3312 size_t iter = 0; | |
3313 SpdyString name, value, lines; | |
3314 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
3315 lines.append(name); | |
3316 lines.append(": "); | |
3317 lines.append(value); | |
3318 lines.append("\n"); | |
3319 } | |
3320 | |
3321 EXPECT_EQ(expected_reply, lines) << i; | |
3322 } | |
3323 } | |
3324 | |
3325 // Verify that we don't crash on invalid response headers. | |
3326 TEST_F(SpdyNetworkTransactionTest, InvalidResponseHeaders) { | |
3327 struct InvalidResponseHeadersTests { | |
3328 int num_headers; | |
3329 const char* headers[10]; | |
3330 } test_cases[] = { | |
3331 // Response headers missing status header | |
3332 { | |
3333 3, | |
3334 {spdy_util_.GetPathKey(), "/index.php", "cookie", "val1", "cookie", | |
3335 "val2", nullptr}, | |
3336 }, | |
3337 // Response headers missing version header | |
3338 { | |
3339 1, {spdy_util_.GetPathKey(), "/index.php", "status", "200", nullptr}, | |
3340 }, | |
3341 // Response headers with no headers | |
3342 { | |
3343 0, {nullptr}, | |
3344 }, | |
3345 }; | |
3346 | |
3347 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
3348 SpdyTestUtil spdy_test_util; | |
3349 | |
3350 SpdySerializedFrame req( | |
3351 spdy_test_util.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3352 SpdySerializedFrame rst( | |
3353 spdy_test_util.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
3354 MockWrite writes[] = { | |
3355 CreateMockWrite(req, 0), CreateMockWrite(rst, 2), | |
3356 }; | |
3357 | |
3358 // Construct the reply. | |
3359 SpdyHeaderBlock reply_headers; | |
3360 AppendToHeaderBlock( | |
3361 test_cases[i].headers, test_cases[i].num_headers, &reply_headers); | |
3362 SpdySerializedFrame resp( | |
3363 spdy_test_util.ConstructSpdyReply(1, std::move(reply_headers))); | |
3364 MockRead reads[] = { | |
3365 CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3) // EOF | |
3366 }; | |
3367 | |
3368 SequencedSocketData data(reads, arraysize(reads), writes, | |
3369 arraysize(writes)); | |
3370 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3371 NetLogWithSource(), nullptr); | |
3372 helper.RunToCompletion(&data); | |
3373 TransactionHelperResult out = helper.output(); | |
3374 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
3375 } | |
3376 } | |
3377 | |
3378 TEST_F(SpdyNetworkTransactionTest, CorruptFrameSessionError) { | |
3379 SpdySerializedFrame req( | |
3380 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3381 SpdySerializedFrame goaway( | |
3382 spdy_util_.ConstructSpdyGoAway(0, ERROR_CODE_COMPRESSION_ERROR, | |
3383 "Framer error: 6 (DECOMPRESS_FAILURE).")); | |
3384 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(goaway, 2)}; | |
3385 | |
3386 // This is the length field that's too short. | |
3387 SpdySerializedFrame reply_wrong_length( | |
3388 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3389 size_t right_size = reply_wrong_length.size() - kFrameHeaderSize; | |
3390 size_t wrong_size = right_size - 4; | |
3391 test::SetFrameLength(&reply_wrong_length, wrong_size); | |
3392 | |
3393 MockRead reads[] = { | |
3394 MockRead(ASYNC, reply_wrong_length.data(), reply_wrong_length.size() - 4, | |
3395 1), | |
3396 }; | |
3397 | |
3398 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3399 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3400 NetLogWithSource(), nullptr); | |
3401 helper.RunToCompletion(&data); | |
3402 TransactionHelperResult out = helper.output(); | |
3403 EXPECT_THAT(out.rv, IsError(ERR_SPDY_COMPRESSION_ERROR)); | |
3404 } | |
3405 | |
3406 TEST_F(SpdyNetworkTransactionTest, GoAwayOnDecompressionFailure) { | |
3407 SpdySerializedFrame req( | |
3408 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3409 SpdySerializedFrame goaway( | |
3410 spdy_util_.ConstructSpdyGoAway(0, ERROR_CODE_COMPRESSION_ERROR, | |
3411 "Framer error: 6 (DECOMPRESS_FAILURE).")); | |
3412 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(goaway, 2)}; | |
3413 | |
3414 // Read HEADERS with corrupted payload. | |
3415 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3416 memset(resp.data() + 12, 0xcf, resp.size() - 12); | |
3417 MockRead reads[] = {CreateMockRead(resp, 1)}; | |
3418 | |
3419 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3420 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3421 NetLogWithSource(), nullptr); | |
3422 helper.RunToCompletion(&data); | |
3423 TransactionHelperResult out = helper.output(); | |
3424 EXPECT_THAT(out.rv, IsError(ERR_SPDY_COMPRESSION_ERROR)); | |
3425 } | |
3426 | |
3427 TEST_F(SpdyNetworkTransactionTest, GoAwayOnFrameSizeError) { | |
3428 SpdySerializedFrame req( | |
3429 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3430 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
3431 0, ERROR_CODE_FRAME_SIZE_ERROR, | |
3432 "Framer error: 15 (INVALID_CONTROL_FRAME_SIZE).")); | |
3433 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(goaway, 2)}; | |
3434 | |
3435 // Read WINDOW_UPDATE with incorrectly-sized payload. | |
3436 SpdySerializedFrame bad_window_update( | |
3437 spdy_util_.ConstructSpdyWindowUpdate(1, 1)); | |
3438 test::SetFrameLength(&bad_window_update, bad_window_update.size() - 1); | |
3439 MockRead reads[] = {CreateMockRead(bad_window_update, 1)}; | |
3440 | |
3441 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3442 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3443 NetLogWithSource(), nullptr); | |
3444 helper.RunToCompletion(&data); | |
3445 TransactionHelperResult out = helper.output(); | |
3446 EXPECT_THAT(out.rv, IsError(ERR_SPDY_FRAME_SIZE_ERROR)); | |
3447 } | |
3448 | |
3449 // Test that we shutdown correctly on write errors. | |
3450 TEST_F(SpdyNetworkTransactionTest, WriteError) { | |
3451 SpdySerializedFrame req( | |
3452 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3453 MockWrite writes[] = { | |
3454 // We'll write 10 bytes successfully | |
3455 MockWrite(ASYNC, req.data(), 10, 1), | |
3456 // Followed by ERROR! | |
3457 MockWrite(ASYNC, ERR_FAILED, 2), | |
3458 // Session drains and attempts to write a GOAWAY: Another ERROR! | |
3459 MockWrite(ASYNC, ERR_FAILED, 3), | |
3460 }; | |
3461 | |
3462 MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; | |
3463 | |
3464 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3465 | |
3466 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3467 NetLogWithSource(), nullptr); | |
3468 helper.RunPreTestSetup(); | |
3469 helper.AddData(&data); | |
3470 EXPECT_TRUE(helper.StartDefaultTest()); | |
3471 helper.FinishDefaultTest(); | |
3472 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
3473 EXPECT_TRUE(data.AllReadDataConsumed()); | |
3474 TransactionHelperResult out = helper.output(); | |
3475 EXPECT_THAT(out.rv, IsError(ERR_FAILED)); | |
3476 } | |
3477 | |
3478 // Test that partial writes work. | |
3479 TEST_F(SpdyNetworkTransactionTest, PartialWrite) { | |
3480 // Chop the HEADERS frame into 5 chunks. | |
3481 SpdySerializedFrame req( | |
3482 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3483 const int kChunks = 5; | |
3484 std::unique_ptr<MockWrite[]> writes(ChopWriteFrame(req, kChunks)); | |
3485 for (int i = 0; i < kChunks; ++i) { | |
3486 writes[i].sequence_number = i; | |
3487 } | |
3488 | |
3489 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3490 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
3491 MockRead reads[] = { | |
3492 CreateMockRead(resp, kChunks), CreateMockRead(body, kChunks + 1), | |
3493 MockRead(ASYNC, 0, kChunks + 2) // EOF | |
3494 }; | |
3495 | |
3496 SequencedSocketData data(reads, arraysize(reads), writes.get(), kChunks); | |
3497 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3498 NetLogWithSource(), nullptr); | |
3499 helper.RunToCompletion(&data); | |
3500 TransactionHelperResult out = helper.output(); | |
3501 EXPECT_THAT(out.rv, IsOk()); | |
3502 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
3503 EXPECT_EQ("hello!", out.response_data); | |
3504 } | |
3505 | |
3506 // Test that the NetLog contains good data for a simple GET request. | |
3507 TEST_F(SpdyNetworkTransactionTest, NetLog) { | |
3508 static const char* const kExtraHeaders[] = { | |
3509 "user-agent", "Chrome", | |
3510 }; | |
3511 SpdySerializedFrame req( | |
3512 spdy_util_.ConstructSpdyGet(kExtraHeaders, 1, 1, LOWEST, true)); | |
3513 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
3514 | |
3515 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3516 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
3517 MockRead reads[] = { | |
3518 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
3519 MockRead(ASYNC, 0, 3) // EOF | |
3520 }; | |
3521 | |
3522 BoundTestNetLog log; | |
3523 | |
3524 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3525 NormalSpdyTransactionHelper helper(CreateGetRequestWithUserAgent(), | |
3526 DEFAULT_PRIORITY, log.bound(), nullptr); | |
3527 helper.RunToCompletion(&data); | |
3528 TransactionHelperResult out = helper.output(); | |
3529 EXPECT_THAT(out.rv, IsOk()); | |
3530 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
3531 EXPECT_EQ("hello!", out.response_data); | |
3532 | |
3533 // Check that the NetLog was filled reasonably. | |
3534 // This test is intentionally non-specific about the exact ordering of the | |
3535 // log; instead we just check to make sure that certain events exist, and that | |
3536 // they are in the right order. | |
3537 TestNetLogEntry::List entries; | |
3538 log.GetEntries(&entries); | |
3539 | |
3540 EXPECT_LT(0u, entries.size()); | |
3541 int pos = 0; | |
3542 pos = ExpectLogContainsSomewhere( | |
3543 entries, 0, NetLogEventType::HTTP_TRANSACTION_SEND_REQUEST, | |
3544 NetLogEventPhase::BEGIN); | |
3545 pos = ExpectLogContainsSomewhere( | |
3546 entries, pos + 1, NetLogEventType::HTTP_TRANSACTION_SEND_REQUEST, | |
3547 NetLogEventPhase::END); | |
3548 pos = ExpectLogContainsSomewhere( | |
3549 entries, pos + 1, NetLogEventType::HTTP_TRANSACTION_READ_HEADERS, | |
3550 NetLogEventPhase::BEGIN); | |
3551 pos = ExpectLogContainsSomewhere( | |
3552 entries, pos + 1, NetLogEventType::HTTP_TRANSACTION_READ_HEADERS, | |
3553 NetLogEventPhase::END); | |
3554 pos = ExpectLogContainsSomewhere(entries, pos + 1, | |
3555 NetLogEventType::HTTP_TRANSACTION_READ_BODY, | |
3556 NetLogEventPhase::BEGIN); | |
3557 pos = ExpectLogContainsSomewhere(entries, pos + 1, | |
3558 NetLogEventType::HTTP_TRANSACTION_READ_BODY, | |
3559 NetLogEventPhase::END); | |
3560 | |
3561 // Check that we logged all the headers correctly | |
3562 pos = ExpectLogContainsSomewhere(entries, 0, | |
3563 NetLogEventType::HTTP2_SESSION_SEND_HEADERS, | |
3564 NetLogEventPhase::NONE); | |
3565 | |
3566 base::ListValue* header_list; | |
3567 ASSERT_TRUE(entries[pos].params.get()); | |
3568 ASSERT_TRUE(entries[pos].params->GetList("headers", &header_list)); | |
3569 | |
3570 std::vector<SpdyString> expected; | |
3571 expected.push_back(SpdyString(spdy_util_.GetHostKey()) + ": www.example.org"); | |
3572 expected.push_back(SpdyString(spdy_util_.GetPathKey()) + ": /"); | |
3573 expected.push_back(SpdyString(spdy_util_.GetSchemeKey()) + ": " + | |
3574 default_url_.scheme()); | |
3575 expected.push_back(SpdyString(spdy_util_.GetMethodKey()) + ": GET"); | |
3576 expected.push_back("user-agent: Chrome"); | |
3577 EXPECT_EQ(expected.size(), header_list->GetSize()); | |
3578 for (std::vector<SpdyString>::const_iterator it = expected.begin(); | |
3579 it != expected.end(); ++it) { | |
3580 base::Value header(*it); | |
3581 EXPECT_NE(header_list->end(), header_list->Find(header)) << | |
3582 "Header not found: " << *it; | |
3583 } | |
3584 } | |
3585 | |
3586 // Since we buffer the IO from the stream to the renderer, this test verifies | |
3587 // that when we read out the maximum amount of data (e.g. we received 50 bytes | |
3588 // on the network, but issued a Read for only 5 of those bytes) that the data | |
3589 // flow still works correctly. | |
3590 TEST_F(SpdyNetworkTransactionTest, BufferFull) { | |
3591 SpdySerializedFrame req( | |
3592 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3593 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
3594 | |
3595 // 2 data frames in a single read. | |
3596 SpdySerializedFrame data_frame_1( | |
3597 spdy_util_.ConstructSpdyDataFrame(1, "goodby", 6, /*fin=*/false)); | |
3598 SpdySerializedFrame data_frame_2( | |
3599 spdy_util_.ConstructSpdyDataFrame(1, "e worl", 6, /*fin=*/false)); | |
3600 const SpdySerializedFrame* data_frames[2] = { | |
3601 &data_frame_1, &data_frame_2, | |
3602 }; | |
3603 char combined_data_frames[100]; | |
3604 int combined_data_frames_len = | |
3605 CombineFrames(data_frames, arraysize(data_frames), | |
3606 combined_data_frames, arraysize(combined_data_frames)); | |
3607 SpdySerializedFrame last_frame( | |
3608 spdy_util_.ConstructSpdyDataFrame(1, "d", 1, /*fin=*/true)); | |
3609 | |
3610 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3611 MockRead reads[] = { | |
3612 CreateMockRead(resp, 1), | |
3613 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause | |
3614 MockRead(ASYNC, combined_data_frames, combined_data_frames_len, 3), | |
3615 MockRead(ASYNC, ERR_IO_PENDING, 4), // Force a pause | |
3616 CreateMockRead(last_frame, 5), | |
3617 MockRead(ASYNC, 0, 6) // EOF | |
3618 }; | |
3619 | |
3620 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3621 | |
3622 TestCompletionCallback callback; | |
3623 | |
3624 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3625 NetLogWithSource(), nullptr); | |
3626 helper.RunPreTestSetup(); | |
3627 helper.AddData(&data); | |
3628 HttpNetworkTransaction* trans = helper.trans(); | |
3629 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
3630 NetLogWithSource()); | |
3631 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
3632 | |
3633 TransactionHelperResult out = helper.output(); | |
3634 out.rv = callback.WaitForResult(); | |
3635 EXPECT_EQ(out.rv, OK); | |
3636 | |
3637 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
3638 EXPECT_TRUE(response->headers); | |
3639 EXPECT_TRUE(response->was_fetched_via_spdy); | |
3640 out.status_line = response->headers->GetStatusLine(); | |
3641 out.response_info = *response; // Make a copy so we can verify. | |
3642 | |
3643 // Read Data | |
3644 TestCompletionCallback read_callback; | |
3645 | |
3646 SpdyString content; | |
3647 do { | |
3648 // Read small chunks at a time. | |
3649 const int kSmallReadSize = 3; | |
3650 scoped_refptr<IOBuffer> buf(new IOBuffer(kSmallReadSize)); | |
3651 rv = trans->Read(buf.get(), kSmallReadSize, read_callback.callback()); | |
3652 if (rv == ERR_IO_PENDING) { | |
3653 data.Resume(); | |
3654 rv = read_callback.WaitForResult(); | |
3655 } | |
3656 if (rv > 0) { | |
3657 content.append(buf->data(), rv); | |
3658 } else if (rv < 0) { | |
3659 NOTREACHED(); | |
3660 } | |
3661 } while (rv > 0); | |
3662 | |
3663 out.response_data.swap(content); | |
3664 | |
3665 // Flush the MessageLoop while the SpdySessionDependencies (in particular, the | |
3666 // MockClientSocketFactory) are still alive. | |
3667 base::RunLoop().RunUntilIdle(); | |
3668 | |
3669 // Verify that we consumed all test data. | |
3670 helper.VerifyDataConsumed(); | |
3671 | |
3672 EXPECT_THAT(out.rv, IsOk()); | |
3673 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
3674 EXPECT_EQ("goodbye world", out.response_data); | |
3675 } | |
3676 | |
3677 // Verify that basic buffering works; when multiple data frames arrive | |
3678 // at the same time, ensure that we don't notify a read completion for | |
3679 // each data frame individually. | |
3680 TEST_F(SpdyNetworkTransactionTest, Buffering) { | |
3681 SpdySerializedFrame req( | |
3682 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3683 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
3684 | |
3685 // 4 data frames in a single read. | |
3686 SpdySerializedFrame data_frame( | |
3687 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/false)); | |
3688 SpdySerializedFrame data_frame_fin( | |
3689 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/true)); | |
3690 const SpdySerializedFrame* data_frames[4] = {&data_frame, &data_frame, | |
3691 &data_frame, &data_frame_fin}; | |
3692 char combined_data_frames[100]; | |
3693 int combined_data_frames_len = | |
3694 CombineFrames(data_frames, arraysize(data_frames), | |
3695 combined_data_frames, arraysize(combined_data_frames)); | |
3696 | |
3697 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3698 MockRead reads[] = { | |
3699 CreateMockRead(resp, 1), | |
3700 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause | |
3701 MockRead(ASYNC, combined_data_frames, combined_data_frames_len, 3), | |
3702 MockRead(ASYNC, 0, 4) // EOF | |
3703 }; | |
3704 | |
3705 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3706 | |
3707 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3708 NetLogWithSource(), nullptr); | |
3709 helper.RunPreTestSetup(); | |
3710 helper.AddData(&data); | |
3711 HttpNetworkTransaction* trans = helper.trans(); | |
3712 | |
3713 TestCompletionCallback callback; | |
3714 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
3715 NetLogWithSource()); | |
3716 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
3717 | |
3718 TransactionHelperResult out = helper.output(); | |
3719 out.rv = callback.WaitForResult(); | |
3720 EXPECT_EQ(out.rv, OK); | |
3721 | |
3722 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
3723 EXPECT_TRUE(response->headers); | |
3724 EXPECT_TRUE(response->was_fetched_via_spdy); | |
3725 out.status_line = response->headers->GetStatusLine(); | |
3726 out.response_info = *response; // Make a copy so we can verify. | |
3727 | |
3728 // Read Data | |
3729 TestCompletionCallback read_callback; | |
3730 | |
3731 SpdyString content; | |
3732 int reads_completed = 0; | |
3733 do { | |
3734 // Read small chunks at a time. | |
3735 const int kSmallReadSize = 14; | |
3736 scoped_refptr<IOBuffer> buf(new IOBuffer(kSmallReadSize)); | |
3737 rv = trans->Read(buf.get(), kSmallReadSize, read_callback.callback()); | |
3738 if (rv == ERR_IO_PENDING) { | |
3739 data.Resume(); | |
3740 rv = read_callback.WaitForResult(); | |
3741 } | |
3742 if (rv > 0) { | |
3743 EXPECT_EQ(kSmallReadSize, rv); | |
3744 content.append(buf->data(), rv); | |
3745 } else if (rv < 0) { | |
3746 FAIL() << "Unexpected read error: " << rv; | |
3747 } | |
3748 reads_completed++; | |
3749 } while (rv > 0); | |
3750 | |
3751 EXPECT_EQ(3, reads_completed); // Reads are: 14 bytes, 14 bytes, 0 bytes. | |
3752 | |
3753 out.response_data.swap(content); | |
3754 | |
3755 // Flush the MessageLoop while the SpdySessionDependencies (in particular, the | |
3756 // MockClientSocketFactory) are still alive. | |
3757 base::RunLoop().RunUntilIdle(); | |
3758 | |
3759 // Verify that we consumed all test data. | |
3760 helper.VerifyDataConsumed(); | |
3761 | |
3762 EXPECT_THAT(out.rv, IsOk()); | |
3763 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
3764 EXPECT_EQ("messagemessagemessagemessage", out.response_data); | |
3765 } | |
3766 | |
3767 // Verify the case where we buffer data but read it after it has been buffered. | |
3768 TEST_F(SpdyNetworkTransactionTest, BufferedAll) { | |
3769 SpdySerializedFrame req( | |
3770 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3771 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
3772 | |
3773 // 5 data frames in a single read. | |
3774 SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3775 SpdySerializedFrame data_frame( | |
3776 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/false)); | |
3777 SpdySerializedFrame data_frame_fin( | |
3778 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/true)); | |
3779 const SpdySerializedFrame* frames[5] = {&reply, &data_frame, &data_frame, | |
3780 &data_frame, &data_frame_fin}; | |
3781 char combined_frames[200]; | |
3782 int combined_frames_len = | |
3783 CombineFrames(frames, arraysize(frames), | |
3784 combined_frames, arraysize(combined_frames)); | |
3785 | |
3786 MockRead reads[] = { | |
3787 MockRead(ASYNC, combined_frames, combined_frames_len, 1), | |
3788 MockRead(ASYNC, 0, 2) // EOF | |
3789 }; | |
3790 | |
3791 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3792 | |
3793 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3794 NetLogWithSource(), nullptr); | |
3795 helper.RunPreTestSetup(); | |
3796 helper.AddData(&data); | |
3797 HttpNetworkTransaction* trans = helper.trans(); | |
3798 | |
3799 TestCompletionCallback callback; | |
3800 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
3801 NetLogWithSource()); | |
3802 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
3803 | |
3804 TransactionHelperResult out = helper.output(); | |
3805 out.rv = callback.WaitForResult(); | |
3806 EXPECT_EQ(out.rv, OK); | |
3807 | |
3808 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
3809 EXPECT_TRUE(response->headers); | |
3810 EXPECT_TRUE(response->was_fetched_via_spdy); | |
3811 out.status_line = response->headers->GetStatusLine(); | |
3812 out.response_info = *response; // Make a copy so we can verify. | |
3813 | |
3814 // Read Data | |
3815 TestCompletionCallback read_callback; | |
3816 | |
3817 SpdyString content; | |
3818 int reads_completed = 0; | |
3819 do { | |
3820 // Read small chunks at a time. | |
3821 const int kSmallReadSize = 14; | |
3822 scoped_refptr<IOBuffer> buf(new IOBuffer(kSmallReadSize)); | |
3823 rv = trans->Read(buf.get(), kSmallReadSize, read_callback.callback()); | |
3824 if (rv > 0) { | |
3825 EXPECT_EQ(kSmallReadSize, rv); | |
3826 content.append(buf->data(), rv); | |
3827 } else if (rv < 0) { | |
3828 FAIL() << "Unexpected read error: " << rv; | |
3829 } | |
3830 reads_completed++; | |
3831 } while (rv > 0); | |
3832 | |
3833 EXPECT_EQ(3, reads_completed); | |
3834 | |
3835 out.response_data.swap(content); | |
3836 | |
3837 // Flush the MessageLoop while the SpdySessionDependencies (in particular, the | |
3838 // MockClientSocketFactory) are still alive. | |
3839 base::RunLoop().RunUntilIdle(); | |
3840 | |
3841 // Verify that we consumed all test data. | |
3842 helper.VerifyDataConsumed(); | |
3843 | |
3844 EXPECT_THAT(out.rv, IsOk()); | |
3845 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
3846 EXPECT_EQ("messagemessagemessagemessage", out.response_data); | |
3847 } | |
3848 | |
3849 // Verify the case where we buffer data and close the connection. | |
3850 TEST_F(SpdyNetworkTransactionTest, BufferedClosed) { | |
3851 SpdySerializedFrame req( | |
3852 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3853 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
3854 | |
3855 // All data frames in a single read. | |
3856 // NOTE: We don't FIN the stream. | |
3857 SpdySerializedFrame data_frame( | |
3858 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/false)); | |
3859 const SpdySerializedFrame* data_frames[4] = {&data_frame, &data_frame, | |
3860 &data_frame, &data_frame}; | |
3861 char combined_data_frames[100]; | |
3862 int combined_data_frames_len = | |
3863 CombineFrames(data_frames, arraysize(data_frames), | |
3864 combined_data_frames, arraysize(combined_data_frames)); | |
3865 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3866 MockRead reads[] = { | |
3867 CreateMockRead(resp, 1), | |
3868 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a wait | |
3869 MockRead(ASYNC, combined_data_frames, combined_data_frames_len, 3), | |
3870 MockRead(ASYNC, 0, 4) // EOF | |
3871 }; | |
3872 | |
3873 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3874 | |
3875 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3876 NetLogWithSource(), nullptr); | |
3877 helper.RunPreTestSetup(); | |
3878 helper.AddData(&data); | |
3879 HttpNetworkTransaction* trans = helper.trans(); | |
3880 | |
3881 TestCompletionCallback callback; | |
3882 | |
3883 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
3884 NetLogWithSource()); | |
3885 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
3886 | |
3887 TransactionHelperResult out = helper.output(); | |
3888 out.rv = callback.WaitForResult(); | |
3889 EXPECT_EQ(out.rv, OK); | |
3890 | |
3891 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
3892 EXPECT_TRUE(response->headers); | |
3893 EXPECT_TRUE(response->was_fetched_via_spdy); | |
3894 out.status_line = response->headers->GetStatusLine(); | |
3895 out.response_info = *response; // Make a copy so we can verify. | |
3896 | |
3897 // Read Data | |
3898 TestCompletionCallback read_callback; | |
3899 | |
3900 SpdyString content; | |
3901 int reads_completed = 0; | |
3902 do { | |
3903 // Read small chunks at a time. | |
3904 const int kSmallReadSize = 14; | |
3905 scoped_refptr<IOBuffer> buf(new IOBuffer(kSmallReadSize)); | |
3906 rv = trans->Read(buf.get(), kSmallReadSize, read_callback.callback()); | |
3907 if (rv == ERR_IO_PENDING) { | |
3908 data.Resume(); | |
3909 rv = read_callback.WaitForResult(); | |
3910 } | |
3911 if (rv > 0) { | |
3912 content.append(buf->data(), rv); | |
3913 } else if (rv < 0) { | |
3914 // This test intentionally closes the connection, and will get an error. | |
3915 EXPECT_THAT(rv, IsError(ERR_CONNECTION_CLOSED)); | |
3916 break; | |
3917 } | |
3918 reads_completed++; | |
3919 } while (rv > 0); | |
3920 | |
3921 EXPECT_EQ(0, reads_completed); | |
3922 | |
3923 out.response_data.swap(content); | |
3924 | |
3925 // Flush the MessageLoop while the SpdySessionDependencies (in particular, the | |
3926 // MockClientSocketFactory) are still alive. | |
3927 base::RunLoop().RunUntilIdle(); | |
3928 | |
3929 // Verify that we consumed all test data. | |
3930 helper.VerifyDataConsumed(); | |
3931 } | |
3932 | |
3933 // Verify the case where we buffer data and cancel the transaction. | |
3934 TEST_F(SpdyNetworkTransactionTest, BufferedCancelled) { | |
3935 SpdySerializedFrame req( | |
3936 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
3937 SpdySerializedFrame rst( | |
3938 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_CANCEL)); | |
3939 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(rst, 4)}; | |
3940 | |
3941 // NOTE: We don't FIN the stream. | |
3942 SpdySerializedFrame data_frame( | |
3943 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/false)); | |
3944 | |
3945 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
3946 MockRead reads[] = { | |
3947 CreateMockRead(resp, 1), | |
3948 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a wait | |
3949 CreateMockRead(data_frame, 3), MockRead(ASYNC, 0, 5) // EOF | |
3950 }; | |
3951 | |
3952 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
3953 | |
3954 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
3955 NetLogWithSource(), nullptr); | |
3956 helper.RunPreTestSetup(); | |
3957 helper.AddData(&data); | |
3958 HttpNetworkTransaction* trans = helper.trans(); | |
3959 TestCompletionCallback callback; | |
3960 | |
3961 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
3962 NetLogWithSource()); | |
3963 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
3964 | |
3965 TransactionHelperResult out = helper.output(); | |
3966 out.rv = callback.WaitForResult(); | |
3967 EXPECT_EQ(out.rv, OK); | |
3968 | |
3969 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
3970 EXPECT_TRUE(response->headers); | |
3971 EXPECT_TRUE(response->was_fetched_via_spdy); | |
3972 out.status_line = response->headers->GetStatusLine(); | |
3973 out.response_info = *response; // Make a copy so we can verify. | |
3974 | |
3975 // Read Data | |
3976 TestCompletionCallback read_callback; | |
3977 | |
3978 const int kReadSize = 256; | |
3979 scoped_refptr<IOBuffer> buf(new IOBuffer(kReadSize)); | |
3980 rv = trans->Read(buf.get(), kReadSize, read_callback.callback()); | |
3981 ASSERT_EQ(ERR_IO_PENDING, rv) << "Unexpected read: " << rv; | |
3982 | |
3983 // Complete the read now, which causes buffering to start. | |
3984 data.Resume(); | |
3985 base::RunLoop().RunUntilIdle(); | |
3986 // Destroy the transaction, causing the stream to get cancelled | |
3987 // and orphaning the buffered IO task. | |
3988 helper.ResetTrans(); | |
3989 | |
3990 // Flush the MessageLoop; this will cause the buffered IO task | |
3991 // to run for the final time. | |
3992 base::RunLoop().RunUntilIdle(); | |
3993 | |
3994 // Verify that we consumed all test data. | |
3995 helper.VerifyDataConsumed(); | |
3996 } | |
3997 | |
3998 // Request should fail upon receiving a GOAWAY frame | |
3999 // with Last-Stream-ID lower than the stream id corresponding to the request | |
4000 // and with error code other than NO_ERROR. | |
4001 TEST_F(SpdyNetworkTransactionTest, FailOnGoAway) { | |
4002 SpdySerializedFrame req( | |
4003 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4004 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
4005 | |
4006 SpdySerializedFrame go_away( | |
4007 spdy_util_.ConstructSpdyGoAway(0, ERROR_CODE_INTERNAL_ERROR, "")); | |
4008 MockRead reads[] = { | |
4009 CreateMockRead(go_away, 1), | |
4010 }; | |
4011 | |
4012 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
4013 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
4014 NetLogWithSource(), nullptr); | |
4015 helper.RunToCompletion(&data); | |
4016 TransactionHelperResult out = helper.output(); | |
4017 EXPECT_THAT(out.rv, IsError(ERR_ABORTED)); | |
4018 } | |
4019 | |
4020 // Request should be retried on a new connection upon receiving a GOAWAY frame | |
4021 // with Last-Stream-ID lower than the stream id corresponding to the request | |
4022 // and with error code NO_ERROR. | |
4023 TEST_F(SpdyNetworkTransactionTest, RetryOnGoAway) { | |
4024 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
4025 NetLogWithSource(), nullptr); | |
4026 | |
4027 // First connection. | |
4028 SpdySerializedFrame req( | |
4029 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4030 MockWrite writes1[] = {CreateMockWrite(req, 0)}; | |
4031 SpdySerializedFrame go_away( | |
4032 spdy_util_.ConstructSpdyGoAway(0, ERROR_CODE_NO_ERROR, "")); | |
4033 MockRead reads1[] = {CreateMockRead(go_away, 1)}; | |
4034 SequencedSocketData data1(reads1, arraysize(reads1), writes1, | |
4035 arraysize(writes1)); | |
4036 helper.AddData(&data1); | |
4037 | |
4038 // Second connection. | |
4039 MockWrite writes2[] = {CreateMockWrite(req, 0)}; | |
4040 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4041 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
4042 MockRead reads2[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
4043 MockRead(ASYNC, 0, 3)}; | |
4044 SequencedSocketData data2(reads2, arraysize(reads2), writes2, | |
4045 arraysize(writes2)); | |
4046 helper.AddData(&data2); | |
4047 | |
4048 helper.RunPreTestSetup(); | |
4049 helper.RunDefaultTest(); | |
4050 | |
4051 TransactionHelperResult out = helper.output(); | |
4052 EXPECT_THAT(out.rv, IsOk()); | |
4053 | |
4054 helper.VerifyDataConsumed(); | |
4055 } | |
4056 | |
4057 // A server can gracefully shut down by sending a GOAWAY frame | |
4058 // with maximum last-stream-id value. | |
4059 // Transactions started before receiving such a GOAWAY frame should succeed, | |
4060 // but SpdySession should be unavailable for new streams. | |
4061 TEST_F(SpdyNetworkTransactionTest, GracefulGoaway) { | |
4062 SpdySerializedFrame req1( | |
4063 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4064 spdy_util_.UpdateWithStreamDestruction(1); | |
4065 SpdySerializedFrame req2( | |
4066 spdy_util_.ConstructSpdyGet("https://www.example.org/foo", 3, LOWEST)); | |
4067 MockWrite writes[] = {CreateMockWrite(req1, 0), CreateMockWrite(req2, 3)}; | |
4068 | |
4069 SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4070 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
4071 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
4072 0x7fffffff, ERROR_CODE_NO_ERROR, "Graceful shutdown.")); | |
4073 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
4074 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
4075 MockRead reads[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2), | |
4076 CreateMockRead(goaway, 4), CreateMockRead(resp2, 5), | |
4077 CreateMockRead(body2, 6), MockRead(ASYNC, 0, 7)}; | |
4078 | |
4079 // Run first transaction. | |
4080 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
4081 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
4082 NetLogWithSource(), nullptr); | |
4083 helper.RunPreTestSetup(); | |
4084 helper.AddData(&data); | |
4085 helper.RunDefaultTest(); | |
4086 | |
4087 // Verify first response. | |
4088 TransactionHelperResult out = helper.output(); | |
4089 EXPECT_THAT(out.rv, IsOk()); | |
4090 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
4091 EXPECT_EQ("hello!", out.response_data); | |
4092 | |
4093 // GOAWAY frame has not yet been received, SpdySession should be available. | |
4094 SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool(); | |
4095 SpdySessionKey key(host_port_pair_, ProxyServer::Direct(), | |
4096 PRIVACY_MODE_DISABLED); | |
4097 NetLogWithSource log; | |
4098 base::WeakPtr<SpdySession> spdy_session = | |
4099 spdy_session_pool->FindAvailableSession( | |
4100 key, GURL(), | |
4101 /* enable_ip_based_pooling = */ true, log); | |
4102 EXPECT_TRUE(spdy_session); | |
4103 | |
4104 // Start second transaction. | |
4105 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
4106 TestCompletionCallback callback; | |
4107 HttpRequestInfo request2; | |
4108 request2.method = "GET"; | |
4109 request2.url = GURL("https://www.example.org/foo"); | |
4110 int rv = trans2.Start(&request2, callback.callback(), log); | |
4111 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
4112 rv = callback.WaitForResult(); | |
4113 EXPECT_THAT(rv, IsOk()); | |
4114 | |
4115 // Verify second response. | |
4116 const HttpResponseInfo* response = trans2.GetResponseInfo(); | |
4117 ASSERT_TRUE(response); | |
4118 EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2, response->connection_info); | |
4119 ASSERT_TRUE(response->headers); | |
4120 EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); | |
4121 EXPECT_TRUE(response->was_fetched_via_spdy); | |
4122 EXPECT_TRUE(response->was_alpn_negotiated); | |
4123 EXPECT_EQ("127.0.0.1", response->socket_address.host()); | |
4124 EXPECT_EQ(443, response->socket_address.port()); | |
4125 SpdyString response_data; | |
4126 rv = ReadTransaction(&trans2, &response_data); | |
4127 EXPECT_THAT(rv, IsOk()); | |
4128 EXPECT_EQ("hello!", response_data); | |
4129 | |
4130 // Graceful GOAWAY was received, SpdySession should be unavailable. | |
4131 spdy_session = spdy_session_pool->FindAvailableSession( | |
4132 key, GURL(), | |
4133 /* enable_ip_based_pooling = */ true, log); | |
4134 EXPECT_FALSE(spdy_session); | |
4135 | |
4136 helper.VerifyDataConsumed(); | |
4137 } | |
4138 | |
4139 TEST_F(SpdyNetworkTransactionTest, CloseWithActiveStream) { | |
4140 SpdySerializedFrame req( | |
4141 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4142 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
4143 | |
4144 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4145 MockRead reads[] = { | |
4146 CreateMockRead(resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF | |
4147 }; | |
4148 | |
4149 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
4150 | |
4151 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
4152 NetLogWithSource(), nullptr); | |
4153 helper.RunPreTestSetup(); | |
4154 helper.AddData(&data); | |
4155 helper.StartDefaultTest(); | |
4156 EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING)); | |
4157 | |
4158 helper.WaitForCallbackToComplete(); | |
4159 EXPECT_THAT(helper.output().rv, IsError(ERR_CONNECTION_CLOSED)); | |
4160 | |
4161 const HttpResponseInfo* response = helper.trans()->GetResponseInfo(); | |
4162 EXPECT_TRUE(response->headers); | |
4163 EXPECT_TRUE(response->was_fetched_via_spdy); | |
4164 | |
4165 // Verify that we consumed all test data. | |
4166 helper.VerifyDataConsumed(); | |
4167 } | |
4168 | |
4169 // Retry with HTTP/1.1 when receiving HTTP_1_1_REQUIRED. Note that no actual | |
4170 // protocol negotiation happens, instead this test forces protocols for both | |
4171 // sockets. | |
4172 TEST_F(SpdyNetworkTransactionTest, HTTP11RequiredRetry) { | |
4173 HttpRequestInfo request; | |
4174 request.method = "GET"; | |
4175 request.url = default_url_; | |
4176 // Do not force SPDY so that second socket can negotiate HTTP/1.1. | |
4177 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
4178 NetLogWithSource(), nullptr); | |
4179 | |
4180 // First socket: HTTP/2 request rejected with HTTP_1_1_REQUIRED. | |
4181 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
4182 SpdySerializedFrame req( | |
4183 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
4184 MockWrite writes0[] = {CreateMockWrite(req, 0)}; | |
4185 SpdySerializedFrame go_away(spdy_util_.ConstructSpdyGoAway( | |
4186 0, ERROR_CODE_HTTP_1_1_REQUIRED, "Try again using HTTP/1.1 please.")); | |
4187 MockRead reads0[] = {CreateMockRead(go_away, 1)}; | |
4188 SequencedSocketData data0(reads0, arraysize(reads0), writes0, | |
4189 arraysize(writes0)); | |
4190 | |
4191 std::unique_ptr<SSLSocketDataProvider> ssl_provider0( | |
4192 new SSLSocketDataProvider(ASYNC, OK)); | |
4193 // Expect HTTP/2 protocols too in SSLConfig. | |
4194 ssl_provider0->next_protos_expected_in_ssl_config.push_back(kProtoHTTP2); | |
4195 ssl_provider0->next_protos_expected_in_ssl_config.push_back(kProtoHTTP11); | |
4196 // Force SPDY. | |
4197 ssl_provider0->next_proto = kProtoHTTP2; | |
4198 helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0)); | |
4199 | |
4200 // Second socket: falling back to HTTP/1.1. | |
4201 MockWrite writes1[] = {MockWrite(ASYNC, 0, | |
4202 "GET / HTTP/1.1\r\n" | |
4203 "Host: www.example.org\r\n" | |
4204 "Connection: keep-alive\r\n\r\n")}; | |
4205 MockRead reads1[] = {MockRead(ASYNC, 1, | |
4206 "HTTP/1.1 200 OK\r\n" | |
4207 "Content-Length: 5\r\n\r\n" | |
4208 "hello")}; | |
4209 SequencedSocketData data1(reads1, arraysize(reads1), writes1, | |
4210 arraysize(writes1)); | |
4211 | |
4212 std::unique_ptr<SSLSocketDataProvider> ssl_provider1( | |
4213 new SSLSocketDataProvider(ASYNC, OK)); | |
4214 // Expect only HTTP/1.1 protocol in SSLConfig. | |
4215 ssl_provider1->next_protos_expected_in_ssl_config.push_back(kProtoHTTP11); | |
4216 // Force HTTP/1.1. | |
4217 ssl_provider1->next_proto = kProtoHTTP11; | |
4218 helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1)); | |
4219 | |
4220 HttpServerProperties* http_server_properties = | |
4221 helper.session()->spdy_session_pool()->http_server_properties(); | |
4222 EXPECT_FALSE(http_server_properties->RequiresHTTP11(host_port_pair_)); | |
4223 | |
4224 helper.RunPreTestSetup(); | |
4225 helper.StartDefaultTest(); | |
4226 helper.FinishDefaultTestWithoutVerification(); | |
4227 helper.VerifyDataConsumed(); | |
4228 EXPECT_TRUE(http_server_properties->RequiresHTTP11(host_port_pair_)); | |
4229 | |
4230 const HttpResponseInfo* response = helper.trans()->GetResponseInfo(); | |
4231 ASSERT_TRUE(response); | |
4232 ASSERT_TRUE(response->headers); | |
4233 EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); | |
4234 EXPECT_FALSE(response->was_fetched_via_spdy); | |
4235 EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1, | |
4236 response->connection_info); | |
4237 EXPECT_TRUE(response->was_alpn_negotiated); | |
4238 EXPECT_TRUE(request.url.SchemeIs("https")); | |
4239 EXPECT_EQ("127.0.0.1", response->socket_address.host()); | |
4240 EXPECT_EQ(443, response->socket_address.port()); | |
4241 SpdyString response_data; | |
4242 ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk()); | |
4243 EXPECT_EQ("hello", response_data); | |
4244 } | |
4245 | |
4246 // Retry with HTTP/1.1 to the proxy when receiving HTTP_1_1_REQUIRED from the | |
4247 // proxy. Note that no actual protocol negotiation happens, instead this test | |
4248 // forces protocols for both sockets. | |
4249 TEST_F(SpdyNetworkTransactionTest, HTTP11RequiredProxyRetry) { | |
4250 HttpRequestInfo request; | |
4251 request.method = "GET"; | |
4252 request.url = default_url_; | |
4253 auto session_deps = base::MakeUnique<SpdySessionDependencies>( | |
4254 ProxyService::CreateFixedFromPacResult("HTTPS myproxy:70")); | |
4255 // Do not force SPDY so that second socket can negotiate HTTP/1.1. | |
4256 NormalSpdyTransactionHelper helper( | |
4257 request, DEFAULT_PRIORITY, NetLogWithSource(), std::move(session_deps)); | |
4258 | |
4259 // First socket: HTTP/2 CONNECT rejected with HTTP_1_1_REQUIRED. | |
4260 SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect( | |
4261 nullptr, 0, 1, LOWEST, HostPortPair("www.example.org", 443))); | |
4262 MockWrite writes0[] = {CreateMockWrite(req, 0)}; | |
4263 SpdySerializedFrame go_away(spdy_util_.ConstructSpdyGoAway( | |
4264 0, ERROR_CODE_HTTP_1_1_REQUIRED, "Try again using HTTP/1.1 please.")); | |
4265 MockRead reads0[] = {CreateMockRead(go_away, 1)}; | |
4266 SequencedSocketData data0(reads0, arraysize(reads0), writes0, | |
4267 arraysize(writes0)); | |
4268 | |
4269 std::unique_ptr<SSLSocketDataProvider> ssl_provider0( | |
4270 new SSLSocketDataProvider(ASYNC, OK)); | |
4271 // Expect HTTP/2 protocols too in SSLConfig. | |
4272 ssl_provider0->next_protos_expected_in_ssl_config.push_back(kProtoHTTP2); | |
4273 ssl_provider0->next_protos_expected_in_ssl_config.push_back(kProtoHTTP11); | |
4274 // Force SPDY. | |
4275 ssl_provider0->next_proto = kProtoHTTP2; | |
4276 helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0)); | |
4277 | |
4278 // Second socket: retry using HTTP/1.1. | |
4279 MockWrite writes1[] = { | |
4280 MockWrite(ASYNC, 0, | |
4281 "CONNECT www.example.org:443 HTTP/1.1\r\n" | |
4282 "Host: www.example.org:443\r\n" | |
4283 "Proxy-Connection: keep-alive\r\n\r\n"), | |
4284 MockWrite(ASYNC, 2, | |
4285 "GET / HTTP/1.1\r\n" | |
4286 "Host: www.example.org\r\n" | |
4287 "Connection: keep-alive\r\n\r\n"), | |
4288 }; | |
4289 | |
4290 MockRead reads1[] = { | |
4291 MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n\r\n"), | |
4292 MockRead(ASYNC, 3, | |
4293 "HTTP/1.1 200 OK\r\n" | |
4294 "Content-Length: 5\r\n\r\n" | |
4295 "hello"), | |
4296 }; | |
4297 SequencedSocketData data1(reads1, arraysize(reads1), writes1, | |
4298 arraysize(writes1)); | |
4299 | |
4300 std::unique_ptr<SSLSocketDataProvider> ssl_provider1( | |
4301 new SSLSocketDataProvider(ASYNC, OK)); | |
4302 // Expect only HTTP/1.1 protocol in SSLConfig. | |
4303 ssl_provider1->next_protos_expected_in_ssl_config.push_back(kProtoHTTP11); | |
4304 // Force HTTP/1.1. | |
4305 ssl_provider1->next_proto = kProtoHTTP11; | |
4306 helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1)); | |
4307 | |
4308 // A third socket is needed for the tunnelled connection. | |
4309 std::unique_ptr<SSLSocketDataProvider> ssl_provider2( | |
4310 new SSLSocketDataProvider(ASYNC, OK)); | |
4311 helper.session_deps()->socket_factory->AddSSLSocketDataProvider( | |
4312 ssl_provider2.get()); | |
4313 | |
4314 HttpServerProperties* http_server_properties = | |
4315 helper.session()->spdy_session_pool()->http_server_properties(); | |
4316 const HostPortPair proxy_host_port_pair = HostPortPair("myproxy", 70); | |
4317 EXPECT_FALSE(http_server_properties->RequiresHTTP11(proxy_host_port_pair)); | |
4318 | |
4319 helper.RunPreTestSetup(); | |
4320 helper.StartDefaultTest(); | |
4321 helper.FinishDefaultTestWithoutVerification(); | |
4322 helper.VerifyDataConsumed(); | |
4323 EXPECT_TRUE(http_server_properties->RequiresHTTP11(proxy_host_port_pair)); | |
4324 | |
4325 const HttpResponseInfo* response = helper.trans()->GetResponseInfo(); | |
4326 ASSERT_TRUE(response); | |
4327 ASSERT_TRUE(response->headers); | |
4328 EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); | |
4329 EXPECT_FALSE(response->was_fetched_via_spdy); | |
4330 EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1, | |
4331 response->connection_info); | |
4332 EXPECT_FALSE(response->was_alpn_negotiated); | |
4333 EXPECT_TRUE(request.url.SchemeIs("https")); | |
4334 EXPECT_EQ("127.0.0.1", response->socket_address.host()); | |
4335 EXPECT_EQ(70, response->socket_address.port()); | |
4336 SpdyString response_data; | |
4337 ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk()); | |
4338 EXPECT_EQ("hello", response_data); | |
4339 } | |
4340 | |
4341 // Test to make sure we can correctly connect through a proxy. | |
4342 TEST_F(SpdyNetworkTransactionTest, ProxyConnect) { | |
4343 auto session_deps = base::MakeUnique<SpdySessionDependencies>( | |
4344 ProxyService::CreateFixedFromPacResult("PROXY myproxy:70")); | |
4345 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
4346 NetLogWithSource(), | |
4347 std::move(session_deps)); | |
4348 helper.RunPreTestSetup(); | |
4349 HttpNetworkTransaction* trans = helper.trans(); | |
4350 | |
4351 const char kConnect443[] = { | |
4352 "CONNECT www.example.org:443 HTTP/1.1\r\n" | |
4353 "Host: www.example.org:443\r\n" | |
4354 "Proxy-Connection: keep-alive\r\n\r\n"}; | |
4355 const char kHTTP200[] = {"HTTP/1.1 200 OK\r\n\r\n"}; | |
4356 SpdySerializedFrame req( | |
4357 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4358 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4359 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
4360 | |
4361 MockWrite writes[] = { | |
4362 MockWrite(SYNCHRONOUS, kConnect443, arraysize(kConnect443) - 1, 0), | |
4363 CreateMockWrite(req, 2), | |
4364 }; | |
4365 MockRead reads[] = { | |
4366 MockRead(SYNCHRONOUS, kHTTP200, arraysize(kHTTP200) - 1, 1), | |
4367 CreateMockRead(resp, 3), CreateMockRead(body, 4), | |
4368 MockRead(ASYNC, 0, 0, 5), | |
4369 }; | |
4370 std::unique_ptr<SequencedSocketData> data(new SequencedSocketData( | |
4371 reads, arraysize(reads), writes, arraysize(writes))); | |
4372 | |
4373 helper.AddData(data.get()); | |
4374 TestCompletionCallback callback; | |
4375 | |
4376 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
4377 NetLogWithSource()); | |
4378 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
4379 | |
4380 rv = callback.WaitForResult(); | |
4381 EXPECT_EQ(0, rv); | |
4382 | |
4383 // Verify the response headers. | |
4384 HttpResponseInfo response = *trans->GetResponseInfo(); | |
4385 ASSERT_TRUE(response.headers); | |
4386 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
4387 | |
4388 SpdyString response_data; | |
4389 ASSERT_THAT(ReadTransaction(trans, &response_data), IsOk()); | |
4390 EXPECT_EQ("hello!", response_data); | |
4391 helper.VerifyDataConsumed(); | |
4392 } | |
4393 | |
4394 // Test to make sure we can correctly connect through a proxy to | |
4395 // www.example.org, if there already exists a direct spdy connection to | |
4396 // www.example.org. See https://crbug.com/49874. | |
4397 TEST_F(SpdyNetworkTransactionTest, DirectConnectProxyReconnect) { | |
4398 // Use a proxy service which returns a proxy fallback list from DIRECT to | |
4399 // myproxy:70. For this test there will be no fallback, so it is equivalent | |
4400 // to simply DIRECT. The reason for appending the second proxy is to verify | |
4401 // that the session pool key used does is just "DIRECT". | |
4402 auto session_deps = base::MakeUnique<SpdySessionDependencies>( | |
4403 ProxyService::CreateFixedFromPacResult("DIRECT; PROXY myproxy:70")); | |
4404 // When setting up the first transaction, we store the SpdySessionPool so that | |
4405 // we can use the same pool in the second transaction. | |
4406 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
4407 NetLogWithSource(), | |
4408 std::move(session_deps)); | |
4409 | |
4410 SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool(); | |
4411 helper.RunPreTestSetup(); | |
4412 | |
4413 // Construct and send a simple GET request. | |
4414 SpdySerializedFrame req( | |
4415 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4416 MockWrite writes[] = { | |
4417 CreateMockWrite(req, 0), | |
4418 }; | |
4419 | |
4420 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4421 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
4422 MockRead reads[] = { | |
4423 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
4424 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3), // Force a pause | |
4425 }; | |
4426 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
4427 helper.AddData(&data); | |
4428 HttpNetworkTransaction* trans = helper.trans(); | |
4429 | |
4430 TestCompletionCallback callback; | |
4431 TransactionHelperResult out; | |
4432 out.rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
4433 NetLogWithSource()); | |
4434 | |
4435 EXPECT_EQ(out.rv, ERR_IO_PENDING); | |
4436 out.rv = callback.WaitForResult(); | |
4437 EXPECT_EQ(out.rv, OK); | |
4438 | |
4439 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
4440 EXPECT_TRUE(response->headers); | |
4441 EXPECT_TRUE(response->was_fetched_via_spdy); | |
4442 out.rv = ReadTransaction(trans, &out.response_data); | |
4443 EXPECT_THAT(out.rv, IsOk()); | |
4444 out.status_line = response->headers->GetStatusLine(); | |
4445 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
4446 EXPECT_EQ("hello!", out.response_data); | |
4447 | |
4448 // Check that the SpdySession is still in the SpdySessionPool. | |
4449 SpdySessionKey session_pool_key_direct(host_port_pair_, ProxyServer::Direct(), | |
4450 PRIVACY_MODE_DISABLED); | |
4451 EXPECT_TRUE(HasSpdySession(spdy_session_pool, session_pool_key_direct)); | |
4452 SpdySessionKey session_pool_key_proxy( | |
4453 host_port_pair_, | |
4454 ProxyServer::FromURI("www.foo.com", ProxyServer::SCHEME_HTTP), | |
4455 PRIVACY_MODE_DISABLED); | |
4456 EXPECT_FALSE(HasSpdySession(spdy_session_pool, session_pool_key_proxy)); | |
4457 | |
4458 // New SpdyTestUtil instance for the session that will be used for the | |
4459 // proxy connection. | |
4460 SpdyTestUtil spdy_util_2; | |
4461 | |
4462 // Set up data for the proxy connection. | |
4463 const char kConnect443[] = { | |
4464 "CONNECT www.example.org:443 HTTP/1.1\r\n" | |
4465 "Host: www.example.org:443\r\n" | |
4466 "Proxy-Connection: keep-alive\r\n\r\n"}; | |
4467 const char kHTTP200[] = {"HTTP/1.1 200 OK\r\n\r\n"}; | |
4468 SpdySerializedFrame req2(spdy_util_2.ConstructSpdyGet( | |
4469 GetDefaultUrlWithPath("/foo.dat").c_str(), 1, LOWEST)); | |
4470 SpdySerializedFrame resp2(spdy_util_2.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4471 SpdySerializedFrame body2(spdy_util_2.ConstructSpdyDataFrame(1, true)); | |
4472 | |
4473 MockWrite writes2[] = { | |
4474 MockWrite(SYNCHRONOUS, kConnect443, arraysize(kConnect443) - 1, 0), | |
4475 CreateMockWrite(req2, 2), | |
4476 }; | |
4477 MockRead reads2[] = { | |
4478 MockRead(SYNCHRONOUS, kHTTP200, arraysize(kHTTP200) - 1, 1), | |
4479 CreateMockRead(resp2, 3), CreateMockRead(body2, 4), | |
4480 MockRead(ASYNC, 0, 5) // EOF | |
4481 }; | |
4482 | |
4483 std::unique_ptr<SequencedSocketData> data_proxy(new SequencedSocketData( | |
4484 reads2, arraysize(reads2), writes2, arraysize(writes2))); | |
4485 | |
4486 // Create another request to www.example.org, but this time through a proxy. | |
4487 HttpRequestInfo request_proxy; | |
4488 request_proxy.method = "GET"; | |
4489 request_proxy.url = GURL(GetDefaultUrlWithPath("/foo.dat")); | |
4490 request_proxy.load_flags = 0; | |
4491 auto session_deps_proxy = base::MakeUnique<SpdySessionDependencies>( | |
4492 ProxyService::CreateFixedFromPacResult("PROXY myproxy:70")); | |
4493 NormalSpdyTransactionHelper helper_proxy(request_proxy, DEFAULT_PRIORITY, | |
4494 NetLogWithSource(), | |
4495 std::move(session_deps_proxy)); | |
4496 helper_proxy.RunPreTestSetup(); | |
4497 helper_proxy.AddData(data_proxy.get()); | |
4498 | |
4499 HttpNetworkTransaction* trans_proxy = helper_proxy.trans(); | |
4500 TestCompletionCallback callback_proxy; | |
4501 int rv = trans_proxy->Start(&request_proxy, callback_proxy.callback(), | |
4502 NetLogWithSource()); | |
4503 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
4504 rv = callback_proxy.WaitForResult(); | |
4505 EXPECT_EQ(0, rv); | |
4506 | |
4507 HttpResponseInfo response_proxy = *trans_proxy->GetResponseInfo(); | |
4508 ASSERT_TRUE(response_proxy.headers); | |
4509 EXPECT_EQ("HTTP/1.1 200", response_proxy.headers->GetStatusLine()); | |
4510 | |
4511 SpdyString response_data; | |
4512 ASSERT_THAT(ReadTransaction(trans_proxy, &response_data), IsOk()); | |
4513 EXPECT_EQ("hello!", response_data); | |
4514 | |
4515 helper_proxy.VerifyDataConsumed(); | |
4516 } | |
4517 | |
4518 // When we get a TCP-level RST, we need to retry a HttpNetworkTransaction | |
4519 // on a new connection, if the connection was previously known to be good. | |
4520 // This can happen when a server reboots without saying goodbye, or when | |
4521 // we're behind a NAT that masked the RST. | |
4522 TEST_F(SpdyNetworkTransactionTest, VerifyRetryOnConnectionReset) { | |
4523 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4524 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
4525 MockRead reads[] = { | |
4526 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
4527 MockRead(ASYNC, ERR_IO_PENDING, 3), | |
4528 MockRead(ASYNC, ERR_CONNECTION_RESET, 4), | |
4529 }; | |
4530 | |
4531 MockRead reads2[] = { | |
4532 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
4533 MockRead(ASYNC, 0, 3) // EOF | |
4534 }; | |
4535 | |
4536 SpdySerializedFrame req( | |
4537 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4538 // In all cases the connection will be reset before req3 can be | |
4539 // dispatched, destroying both streams. | |
4540 spdy_util_.UpdateWithStreamDestruction(1); | |
4541 SpdySerializedFrame req3( | |
4542 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
4543 MockWrite writes1[] = {CreateMockWrite(req, 0), CreateMockWrite(req3, 5)}; | |
4544 MockWrite writes2[] = {CreateMockWrite(req, 0)}; | |
4545 | |
4546 // This test has a couple of variants. | |
4547 enum { | |
4548 // Induce the RST while waiting for our transaction to send. | |
4549 VARIANT_RST_DURING_SEND_COMPLETION = 0, | |
4550 // Induce the RST while waiting for our transaction to read. | |
4551 // In this case, the send completed - everything copied into the SNDBUF. | |
4552 VARIANT_RST_DURING_READ_COMPLETION = 1 | |
4553 }; | |
4554 | |
4555 for (int variant = VARIANT_RST_DURING_SEND_COMPLETION; | |
4556 variant <= VARIANT_RST_DURING_READ_COMPLETION; | |
4557 ++variant) { | |
4558 SequencedSocketData data1(reads, arraysize(reads), writes1, 1 + variant); | |
4559 | |
4560 SequencedSocketData data2(reads2, arraysize(reads2), writes2, | |
4561 arraysize(writes2)); | |
4562 | |
4563 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
4564 NetLogWithSource(), nullptr); | |
4565 helper.AddData(&data1); | |
4566 helper.AddData(&data2); | |
4567 helper.RunPreTestSetup(); | |
4568 | |
4569 for (int i = 0; i < 2; ++i) { | |
4570 HttpNetworkTransaction trans(DEFAULT_PRIORITY, helper.session()); | |
4571 | |
4572 TestCompletionCallback callback; | |
4573 int rv = trans.Start(&helper.request(), callback.callback(), | |
4574 NetLogWithSource()); | |
4575 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
4576 // On the second transaction, we trigger the RST. | |
4577 if (i == 1) { | |
4578 if (variant == VARIANT_RST_DURING_READ_COMPLETION) { | |
4579 // Writes to the socket complete asynchronously on SPDY by running | |
4580 // through the message loop. Complete the write here. | |
4581 base::RunLoop().RunUntilIdle(); | |
4582 } | |
4583 | |
4584 // Now schedule the ERR_CONNECTION_RESET. | |
4585 data1.Resume(); | |
4586 } | |
4587 rv = callback.WaitForResult(); | |
4588 EXPECT_THAT(rv, IsOk()); | |
4589 | |
4590 const HttpResponseInfo* response = trans.GetResponseInfo(); | |
4591 ASSERT_TRUE(response); | |
4592 EXPECT_TRUE(response->headers); | |
4593 EXPECT_TRUE(response->was_fetched_via_spdy); | |
4594 SpdyString response_data; | |
4595 rv = ReadTransaction(&trans, &response_data); | |
4596 EXPECT_THAT(rv, IsOk()); | |
4597 EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); | |
4598 EXPECT_EQ("hello!", response_data); | |
4599 base::RunLoop().RunUntilIdle(); | |
4600 } | |
4601 | |
4602 helper.VerifyDataConsumed(); | |
4603 base::RunLoop().RunUntilIdle(); | |
4604 } | |
4605 } | |
4606 | |
4607 // Tests that Basic authentication works over SPDY | |
4608 TEST_F(SpdyNetworkTransactionTest, SpdyBasicAuth) { | |
4609 // The first request will be a bare GET, the second request will be a | |
4610 // GET with an Authorization header. | |
4611 SpdySerializedFrame req_get( | |
4612 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4613 // Will be refused for lack of auth. | |
4614 spdy_util_.UpdateWithStreamDestruction(1); | |
4615 const char* const kExtraAuthorizationHeaders[] = { | |
4616 "authorization", "Basic Zm9vOmJhcg==" | |
4617 }; | |
4618 SpdySerializedFrame req_get_authorization(spdy_util_.ConstructSpdyGet( | |
4619 kExtraAuthorizationHeaders, arraysize(kExtraAuthorizationHeaders) / 2, 3, | |
4620 LOWEST, true)); | |
4621 MockWrite spdy_writes[] = { | |
4622 CreateMockWrite(req_get, 0), CreateMockWrite(req_get_authorization, 3), | |
4623 }; | |
4624 | |
4625 // The first response is a 401 authentication challenge, and the second | |
4626 // response will be a 200 response since the second request includes a valid | |
4627 // Authorization header. | |
4628 const char* const kExtraAuthenticationHeaders[] = { | |
4629 "www-authenticate", | |
4630 "Basic realm=\"MyRealm\"" | |
4631 }; | |
4632 SpdySerializedFrame resp_authentication(spdy_util_.ConstructSpdyReplyError( | |
4633 "401", kExtraAuthenticationHeaders, | |
4634 arraysize(kExtraAuthenticationHeaders) / 2, 1)); | |
4635 SpdySerializedFrame body_authentication( | |
4636 spdy_util_.ConstructSpdyDataFrame(1, true)); | |
4637 SpdySerializedFrame resp_data( | |
4638 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
4639 SpdySerializedFrame body_data(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
4640 | |
4641 MockRead spdy_reads[] = { | |
4642 CreateMockRead(resp_authentication, 1), | |
4643 CreateMockRead(body_authentication, 2, SYNCHRONOUS), | |
4644 CreateMockRead(resp_data, 4), | |
4645 CreateMockRead(body_data, 5), | |
4646 MockRead(ASYNC, 0, 6), | |
4647 }; | |
4648 | |
4649 SequencedSocketData data(spdy_reads, arraysize(spdy_reads), spdy_writes, | |
4650 arraysize(spdy_writes)); | |
4651 HttpRequestInfo request(CreateGetRequest()); | |
4652 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
4653 NetLogWithSource(), nullptr); | |
4654 | |
4655 helper.RunPreTestSetup(); | |
4656 helper.AddData(&data); | |
4657 helper.StartDefaultTest(); | |
4658 EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING)); | |
4659 | |
4660 helper.WaitForCallbackToComplete(); | |
4661 EXPECT_THAT(helper.output().rv, IsOk()); | |
4662 | |
4663 // Make sure the response has an auth challenge. | |
4664 HttpNetworkTransaction* trans = helper.trans(); | |
4665 const HttpResponseInfo* const response_start = trans->GetResponseInfo(); | |
4666 ASSERT_TRUE(response_start); | |
4667 ASSERT_TRUE(response_start->headers); | |
4668 EXPECT_EQ(401, response_start->headers->response_code()); | |
4669 EXPECT_TRUE(response_start->was_fetched_via_spdy); | |
4670 AuthChallengeInfo* auth_challenge = response_start->auth_challenge.get(); | |
4671 ASSERT_TRUE(auth_challenge); | |
4672 EXPECT_FALSE(auth_challenge->is_proxy); | |
4673 EXPECT_EQ(kBasicAuthScheme, auth_challenge->scheme); | |
4674 EXPECT_EQ("MyRealm", auth_challenge->realm); | |
4675 | |
4676 // Restart with a username/password. | |
4677 AuthCredentials credentials(base::ASCIIToUTF16("foo"), | |
4678 base::ASCIIToUTF16("bar")); | |
4679 TestCompletionCallback callback_restart; | |
4680 const int rv_restart = trans->RestartWithAuth( | |
4681 credentials, callback_restart.callback()); | |
4682 EXPECT_THAT(rv_restart, IsError(ERR_IO_PENDING)); | |
4683 const int rv_restart_complete = callback_restart.WaitForResult(); | |
4684 EXPECT_THAT(rv_restart_complete, IsOk()); | |
4685 // TODO(cbentzel): This is actually the same response object as before, but | |
4686 // data has changed. | |
4687 const HttpResponseInfo* const response_restart = trans->GetResponseInfo(); | |
4688 ASSERT_TRUE(response_restart); | |
4689 ASSERT_TRUE(response_restart->headers); | |
4690 EXPECT_EQ(200, response_restart->headers->response_code()); | |
4691 EXPECT_TRUE(response_restart->auth_challenge.get() == nullptr); | |
4692 } | |
4693 | |
4694 TEST_F(SpdyNetworkTransactionTest, ServerPushWithHeaders) { | |
4695 SpdySerializedFrame stream1_syn( | |
4696 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4697 SpdySerializedFrame stream2_priority( | |
4698 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
4699 MockWrite writes[] = { | |
4700 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
4701 }; | |
4702 | |
4703 SpdySerializedFrame stream1_reply( | |
4704 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4705 | |
4706 SpdyHeaderBlock initial_headers; | |
4707 spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat"), | |
4708 &initial_headers); | |
4709 SpdySerializedFrame stream2_syn(spdy_util_.ConstructInitialSpdyPushFrame( | |
4710 std::move(initial_headers), 2, 1)); | |
4711 | |
4712 SpdyHeaderBlock late_headers; | |
4713 late_headers[spdy_util_.GetStatusKey()] = "200"; | |
4714 late_headers["hello"] = "bye"; | |
4715 SpdySerializedFrame stream2_headers(spdy_util_.ConstructSpdyResponseHeaders( | |
4716 2, std::move(late_headers), false)); | |
4717 | |
4718 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
4719 | |
4720 const char kPushedData[] = "pushed"; | |
4721 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
4722 2, kPushedData, strlen(kPushedData), true)); | |
4723 | |
4724 MockRead reads[] = { | |
4725 CreateMockRead(stream1_reply, 1), | |
4726 CreateMockRead(stream2_syn, 2), | |
4727 CreateMockRead(stream2_headers, 4), | |
4728 CreateMockRead(stream1_body, 5, SYNCHRONOUS), | |
4729 CreateMockRead(stream2_body, 6), | |
4730 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 7), // Force a pause | |
4731 }; | |
4732 | |
4733 HttpResponseInfo response; | |
4734 HttpResponseInfo response2; | |
4735 SpdyString expected_push_result("pushed"); | |
4736 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
4737 RunServerPushTest(&data, | |
4738 &response, | |
4739 &response2, | |
4740 expected_push_result); | |
4741 | |
4742 // Verify the response headers. | |
4743 EXPECT_TRUE(response.headers); | |
4744 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
4745 | |
4746 // Verify the pushed stream. | |
4747 EXPECT_TRUE(response2.headers); | |
4748 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
4749 } | |
4750 | |
4751 TEST_F(SpdyNetworkTransactionTest, ServerPushClaimBeforeHeaders) { | |
4752 // We push a stream and attempt to claim it before the headers come down. | |
4753 SpdySerializedFrame stream1_syn( | |
4754 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4755 SpdySerializedFrame stream2_priority( | |
4756 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
4757 MockWrite writes[] = { | |
4758 CreateMockWrite(stream1_syn, 0, SYNCHRONOUS), | |
4759 CreateMockWrite(stream2_priority, 3), | |
4760 }; | |
4761 | |
4762 SpdySerializedFrame stream1_reply( | |
4763 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4764 SpdyHeaderBlock initial_headers; | |
4765 spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat"), | |
4766 &initial_headers); | |
4767 SpdySerializedFrame stream2_syn(spdy_util_.ConstructInitialSpdyPushFrame( | |
4768 std::move(initial_headers), 2, 1)); | |
4769 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
4770 SpdyHeaderBlock late_headers; | |
4771 late_headers[spdy_util_.GetStatusKey()] = "200"; | |
4772 late_headers["hello"] = "bye"; | |
4773 SpdySerializedFrame stream2_headers(spdy_util_.ConstructSpdyResponseHeaders( | |
4774 2, std::move(late_headers), false)); | |
4775 const char kPushedData[] = "pushed"; | |
4776 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
4777 2, kPushedData, strlen(kPushedData), true)); | |
4778 MockRead reads[] = { | |
4779 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
4780 CreateMockRead(stream1_body, 4), MockRead(ASYNC, ERR_IO_PENDING, 5), | |
4781 CreateMockRead(stream2_headers, 6), CreateMockRead(stream2_body, 7), | |
4782 MockRead(ASYNC, ERR_IO_PENDING, 8), MockRead(ASYNC, 0, 9), // EOF | |
4783 }; | |
4784 | |
4785 HttpResponseInfo response; | |
4786 HttpResponseInfo response2; | |
4787 SpdyString expected_push_result("pushed"); | |
4788 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
4789 | |
4790 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
4791 NetLogWithSource(), nullptr); | |
4792 helper.AddData(&data); | |
4793 helper.RunPreTestSetup(); | |
4794 | |
4795 HttpNetworkTransaction* trans = helper.trans(); | |
4796 | |
4797 // Start the transaction. | |
4798 TestCompletionCallback callback; | |
4799 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
4800 NetLogWithSource()); | |
4801 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
4802 // Run until we've received the primary HEADERS, the pushed HEADERS, | |
4803 // and the body of the primary stream, but before we've received the HEADERS | |
4804 // for the pushed stream. | |
4805 data.RunUntilPaused(); | |
4806 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
4807 | |
4808 // Request the pushed path. At this point, we've received the push, but the | |
4809 // headers are not yet complete. | |
4810 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
4811 rv = trans2.Start(&CreateGetPushRequest(), callback.callback(), | |
4812 NetLogWithSource()); | |
4813 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
4814 data.Resume(); | |
4815 data.RunUntilPaused(); | |
4816 base::RunLoop().RunUntilIdle(); | |
4817 | |
4818 // Read the server push body. | |
4819 SpdyString result2; | |
4820 ReadResult(&trans2, &result2); | |
4821 // Read the response body. | |
4822 SpdyString result; | |
4823 ReadResult(trans, &result); | |
4824 | |
4825 // Verify that the received push data is same as the expected push data. | |
4826 EXPECT_EQ(result2.compare(expected_push_result), 0) | |
4827 << "Received data: " | |
4828 << result2 | |
4829 << "||||| Expected data: " | |
4830 << expected_push_result; | |
4831 | |
4832 // Verify the response headers. | |
4833 // Copy the response info, because trans goes away. | |
4834 response = *trans->GetResponseInfo(); | |
4835 response2 = *trans2.GetResponseInfo(); | |
4836 | |
4837 VerifyStreamsClosed(helper); | |
4838 | |
4839 // Verify the response headers. | |
4840 EXPECT_TRUE(response.headers); | |
4841 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
4842 | |
4843 // Verify the pushed stream. | |
4844 EXPECT_TRUE(response2.headers); | |
4845 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
4846 | |
4847 // Read the final EOF (which will close the session) | |
4848 data.Resume(); | |
4849 base::RunLoop().RunUntilIdle(); | |
4850 | |
4851 // Verify that we consumed all test data. | |
4852 EXPECT_TRUE(data.AllReadDataConsumed()); | |
4853 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
4854 } | |
4855 | |
4856 TEST_F(SpdyNetworkTransactionTest, ResponseHeadersTwice) { | |
4857 SpdySerializedFrame req( | |
4858 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4859 SpdySerializedFrame rst( | |
4860 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
4861 MockWrite writes[] = { | |
4862 CreateMockWrite(req, 0), CreateMockWrite(rst, 4), | |
4863 }; | |
4864 | |
4865 SpdySerializedFrame stream1_reply( | |
4866 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4867 | |
4868 SpdyHeaderBlock late_headers; | |
4869 late_headers["hello"] = "bye"; | |
4870 SpdySerializedFrame stream1_headers(spdy_util_.ConstructSpdyResponseHeaders( | |
4871 1, std::move(late_headers), false)); | |
4872 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
4873 MockRead reads[] = { | |
4874 CreateMockRead(stream1_reply, 1), CreateMockRead(stream1_headers, 2), | |
4875 CreateMockRead(stream1_body, 3), MockRead(ASYNC, 0, 5) // EOF | |
4876 }; | |
4877 | |
4878 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
4879 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
4880 NetLogWithSource(), nullptr); | |
4881 helper.RunToCompletion(&data); | |
4882 TransactionHelperResult out = helper.output(); | |
4883 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
4884 } | |
4885 | |
4886 // Tests that receiving HEADERS, DATA, HEADERS, and DATA in that sequence will | |
4887 // trigger a ERR_SPDY_PROTOCOL_ERROR because trailing HEADERS must not be | |
4888 // followed by any DATA frames. | |
4889 TEST_F(SpdyNetworkTransactionTest, SyncReplyDataAfterTrailers) { | |
4890 SpdySerializedFrame req( | |
4891 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
4892 SpdySerializedFrame rst( | |
4893 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
4894 MockWrite writes[] = { | |
4895 CreateMockWrite(req, 0), CreateMockWrite(rst, 5), | |
4896 }; | |
4897 | |
4898 SpdySerializedFrame stream1_reply( | |
4899 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4900 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
4901 | |
4902 SpdyHeaderBlock late_headers; | |
4903 late_headers["hello"] = "bye"; | |
4904 SpdySerializedFrame stream1_headers(spdy_util_.ConstructSpdyResponseHeaders( | |
4905 1, std::move(late_headers), false)); | |
4906 SpdySerializedFrame stream1_body2(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
4907 MockRead reads[] = { | |
4908 CreateMockRead(stream1_reply, 1), CreateMockRead(stream1_body, 2), | |
4909 CreateMockRead(stream1_headers, 3), CreateMockRead(stream1_body2, 4), | |
4910 MockRead(ASYNC, 0, 6) // EOF | |
4911 }; | |
4912 | |
4913 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
4914 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
4915 NetLogWithSource(), nullptr); | |
4916 helper.RunToCompletion(&data); | |
4917 TransactionHelperResult out = helper.output(); | |
4918 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
4919 } | |
4920 | |
4921 TEST_F(SpdyNetworkTransactionTest, ServerPushCrossOriginCorrectness) { | |
4922 // In this test we want to verify that we can't accidentally push content | |
4923 // which can't be pushed by this content server. | |
4924 // This test assumes that: | |
4925 // - if we're requesting http://www.foo.com/barbaz | |
4926 // - the browser has made a connection to "www.foo.com". | |
4927 | |
4928 // A list of the URL to fetch, followed by the URL being pushed. | |
4929 static const char* const kTestCases[] = { | |
4930 "https://www.example.org/foo.html", | |
4931 "http://www.example.org/foo.js", // Bad protocol | |
4932 | |
4933 "https://www.example.org/foo.html", | |
4934 "ftp://www.example.org/foo.js", // Invalid Protocol | |
4935 | |
4936 "https://www.example.org/foo.html", | |
4937 "https://blat.www.example.org/foo.js", // Cross subdomain | |
4938 | |
4939 "https://www.example.org/foo.html", | |
4940 "https://www.foo.com/foo.js", // Cross domain | |
4941 }; | |
4942 | |
4943 for (size_t index = 0; index < arraysize(kTestCases); index += 2) { | |
4944 const char* url_to_fetch = kTestCases[index]; | |
4945 const char* url_to_push = kTestCases[index + 1]; | |
4946 | |
4947 SpdyTestUtil spdy_test_util; | |
4948 SpdySerializedFrame stream1_syn( | |
4949 spdy_test_util.ConstructSpdyGet(url_to_fetch, 1, LOWEST)); | |
4950 SpdySerializedFrame stream1_body( | |
4951 spdy_test_util.ConstructSpdyDataFrame(1, true)); | |
4952 SpdySerializedFrame push_rst( | |
4953 spdy_test_util.ConstructSpdyRstStream(2, ERROR_CODE_REFUSED_STREAM)); | |
4954 MockWrite writes[] = { | |
4955 CreateMockWrite(stream1_syn, 0), CreateMockWrite(push_rst, 3), | |
4956 }; | |
4957 | |
4958 SpdySerializedFrame stream1_reply( | |
4959 spdy_test_util.ConstructSpdyGetReply(nullptr, 0, 1)); | |
4960 SpdySerializedFrame stream2_syn( | |
4961 spdy_test_util.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push)); | |
4962 const char kPushedData[] = "pushed"; | |
4963 SpdySerializedFrame stream2_body(spdy_test_util.ConstructSpdyDataFrame( | |
4964 2, kPushedData, strlen(kPushedData), true)); | |
4965 SpdySerializedFrame rst( | |
4966 spdy_test_util.ConstructSpdyRstStream(2, ERROR_CODE_CANCEL)); | |
4967 | |
4968 MockRead reads[] = { | |
4969 CreateMockRead(stream1_reply, 1), | |
4970 CreateMockRead(stream2_syn, 2), | |
4971 CreateMockRead(stream1_body, 4), | |
4972 CreateMockRead(stream2_body, 5), | |
4973 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause | |
4974 }; | |
4975 | |
4976 HttpResponseInfo response; | |
4977 SequencedSocketData data(reads, arraysize(reads), writes, | |
4978 arraysize(writes)); | |
4979 | |
4980 HttpRequestInfo request; | |
4981 request.method = "GET"; | |
4982 request.url = GURL(url_to_fetch); | |
4983 request.load_flags = 0; | |
4984 | |
4985 // Enable cross-origin push. Since we are not using a proxy, this should | |
4986 // not actually enable cross-origin SPDY push. | |
4987 auto session_deps = base::MakeUnique<SpdySessionDependencies>(); | |
4988 std::unique_ptr<TestProxyDelegate> proxy_delegate(new TestProxyDelegate()); | |
4989 proxy_delegate->set_trusted_spdy_proxy(net::ProxyServer::FromURI( | |
4990 "https://123.45.67.89:443", net::ProxyServer::SCHEME_HTTP)); | |
4991 session_deps->proxy_delegate = std::move(proxy_delegate); | |
4992 NormalSpdyTransactionHelper helper( | |
4993 request, DEFAULT_PRIORITY, NetLogWithSource(), std::move(session_deps)); | |
4994 helper.RunPreTestSetup(); | |
4995 helper.AddData(&data); | |
4996 | |
4997 HttpNetworkTransaction* trans = helper.trans(); | |
4998 | |
4999 // Start the transaction with basic parameters. | |
5000 TestCompletionCallback callback; | |
5001 | |
5002 int rv = trans->Start(&request, callback.callback(), NetLogWithSource()); | |
5003 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
5004 rv = callback.WaitForResult(); | |
5005 | |
5006 // Finish async network reads/writes. | |
5007 base::RunLoop().RunUntilIdle(); | |
5008 | |
5009 // Read the response body. | |
5010 SpdyString result; | |
5011 ReadResult(trans, &result); | |
5012 | |
5013 // Verify that we consumed all test data. | |
5014 EXPECT_TRUE(data.AllReadDataConsumed()); | |
5015 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
5016 | |
5017 // Verify the response headers. | |
5018 // Copy the response info, because trans goes away. | |
5019 response = *trans->GetResponseInfo(); | |
5020 | |
5021 VerifyStreamsClosed(helper); | |
5022 | |
5023 // Verify the response headers. | |
5024 EXPECT_TRUE(response.headers); | |
5025 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
5026 } | |
5027 } | |
5028 | |
5029 // Verify that push works cross origin as long as the certificate is valid for | |
5030 // the pushed authority. | |
5031 TEST_F(SpdyNetworkTransactionTest, ServerPushValidCrossOrigin) { | |
5032 // "spdy_pooling.pem" is valid for both www.example.org and mail.example.org. | |
5033 const char* url_to_fetch = "https://www.example.org"; | |
5034 const char* url_to_push = "https://mail.example.org"; | |
5035 | |
5036 SpdySerializedFrame headers( | |
5037 spdy_util_.ConstructSpdyGet(url_to_fetch, 1, LOWEST)); | |
5038 SpdySerializedFrame push_priority( | |
5039 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
5040 MockWrite writes[] = { | |
5041 CreateMockWrite(headers, 0), CreateMockWrite(push_priority, 3), | |
5042 }; | |
5043 | |
5044 SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
5045 SpdySerializedFrame push( | |
5046 spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push)); | |
5047 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
5048 const char kPushedData[] = "pushed"; | |
5049 SpdySerializedFrame pushed_body(spdy_util_.ConstructSpdyDataFrame( | |
5050 2, kPushedData, strlen(kPushedData), true)); | |
5051 MockRead reads[] = { | |
5052 CreateMockRead(reply, 1), | |
5053 CreateMockRead(push, 2, SYNCHRONOUS), | |
5054 CreateMockRead(body, 4), | |
5055 CreateMockRead(pushed_body, 5, SYNCHRONOUS), | |
5056 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), | |
5057 }; | |
5058 | |
5059 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
5060 | |
5061 HttpRequestInfo request; | |
5062 request.method = "GET"; | |
5063 request.url = GURL(url_to_fetch); | |
5064 request.load_flags = 0; | |
5065 | |
5066 NetLogWithSource log; | |
5067 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, log, nullptr); | |
5068 helper.RunPreTestSetup(); | |
5069 helper.AddData(&data); | |
5070 | |
5071 HttpNetworkTransaction* trans0 = helper.trans(); | |
5072 TestCompletionCallback callback0; | |
5073 int rv = trans0->Start(&request, callback0.callback(), log); | |
5074 rv = callback0.GetResult(rv); | |
5075 EXPECT_THAT(rv, IsOk()); | |
5076 | |
5077 SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool(); | |
5078 SpdySessionKey key(host_port_pair_, ProxyServer::Direct(), | |
5079 PRIVACY_MODE_DISABLED); | |
5080 base::WeakPtr<SpdySession> spdy_session = | |
5081 spdy_session_pool->FindAvailableSession( | |
5082 key, GURL(), | |
5083 /* enable_ip_based_pooling = */ true, log); | |
5084 | |
5085 EXPECT_FALSE(spdy_session->unclaimed_pushed_streams_.empty()); | |
5086 EXPECT_EQ(1u, spdy_session->unclaimed_pushed_streams_.size()); | |
5087 EXPECT_EQ(1u, | |
5088 spdy_session->unclaimed_pushed_streams_.count(GURL(url_to_push))); | |
5089 | |
5090 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
5091 HttpRequestInfo push_request; | |
5092 push_request.method = "GET"; | |
5093 push_request.url = GURL(url_to_push); | |
5094 push_request.load_flags = 0; | |
5095 TestCompletionCallback callback1; | |
5096 rv = trans1.Start(&push_request, callback1.callback(), log); | |
5097 rv = callback1.GetResult(rv); | |
5098 EXPECT_THAT(rv, IsOk()); | |
5099 | |
5100 EXPECT_TRUE(spdy_session->unclaimed_pushed_streams_.empty()); | |
5101 EXPECT_EQ(0u, spdy_session->unclaimed_pushed_streams_.size()); | |
5102 | |
5103 HttpResponseInfo response = *trans0->GetResponseInfo(); | |
5104 EXPECT_TRUE(response.headers); | |
5105 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
5106 | |
5107 SpdyString result0; | |
5108 ReadResult(trans0, &result0); | |
5109 EXPECT_EQ("hello!", result0); | |
5110 | |
5111 HttpResponseInfo push_response = *trans1.GetResponseInfo(); | |
5112 EXPECT_TRUE(push_response.headers); | |
5113 EXPECT_EQ("HTTP/1.1 200", push_response.headers->GetStatusLine()); | |
5114 | |
5115 SpdyString result1; | |
5116 ReadResult(&trans1, &result1); | |
5117 EXPECT_EQ(kPushedData, result1); | |
5118 | |
5119 base::RunLoop().RunUntilIdle(); | |
5120 helper.VerifyDataConsumed(); | |
5121 VerifyStreamsClosed(helper); | |
5122 } | |
5123 | |
5124 // Verify that push works cross origin, even if there is already a connection | |
5125 // open to origin of pushed resource. | |
5126 TEST_F(SpdyNetworkTransactionTest, ServerPushValidCrossOriginWithOpenSession) { | |
5127 const char* url_to_fetch0 = "https://mail.example.org/foo"; | |
5128 const char* url_to_fetch1 = "https://docs.example.org"; | |
5129 const char* url_to_push = "https://mail.example.org/bar"; | |
5130 | |
5131 SpdyTestUtil spdy_util_0; | |
5132 | |
5133 SpdySerializedFrame headers0( | |
5134 spdy_util_0.ConstructSpdyGet(url_to_fetch0, 1, LOWEST)); | |
5135 MockWrite writes0[] = { | |
5136 CreateMockWrite(headers0, 0), | |
5137 }; | |
5138 | |
5139 SpdySerializedFrame reply0(spdy_util_0.ConstructSpdyGetReply(nullptr, 0, 1)); | |
5140 const char kData0[] = "first"; | |
5141 SpdySerializedFrame body0( | |
5142 spdy_util_0.ConstructSpdyDataFrame(1, kData0, strlen(kData0), true)); | |
5143 MockRead reads0[] = {CreateMockRead(reply0, 1), CreateMockRead(body0, 2), | |
5144 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)}; | |
5145 | |
5146 SequencedSocketData data0(reads0, arraysize(reads0), writes0, | |
5147 arraysize(writes0)); | |
5148 | |
5149 SpdyTestUtil spdy_util_1; | |
5150 | |
5151 SpdySerializedFrame headers1( | |
5152 spdy_util_1.ConstructSpdyGet(url_to_fetch1, 1, LOWEST)); | |
5153 SpdySerializedFrame push_priority( | |
5154 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
5155 MockWrite writes1[] = { | |
5156 CreateMockWrite(headers1, 0), | |
5157 CreateMockWrite(push_priority, 3, SYNCHRONOUS), | |
5158 }; | |
5159 | |
5160 SpdySerializedFrame reply1(spdy_util_1.ConstructSpdyGetReply(nullptr, 0, 1)); | |
5161 SpdySerializedFrame push( | |
5162 spdy_util_1.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push)); | |
5163 const char kData1[] = "second"; | |
5164 SpdySerializedFrame body1( | |
5165 spdy_util_1.ConstructSpdyDataFrame(1, kData1, strlen(kData1), true)); | |
5166 const char kPushedData[] = "pushed"; | |
5167 SpdySerializedFrame pushed_body(spdy_util_1.ConstructSpdyDataFrame( | |
5168 2, kPushedData, strlen(kPushedData), true)); | |
5169 | |
5170 MockRead reads1[] = { | |
5171 CreateMockRead(reply1, 1), | |
5172 CreateMockRead(push, 2, SYNCHRONOUS), | |
5173 CreateMockRead(body1, 4), | |
5174 CreateMockRead(pushed_body, 5, SYNCHRONOUS), | |
5175 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), | |
5176 }; | |
5177 | |
5178 SequencedSocketData data1(reads1, arraysize(reads1), writes1, | |
5179 arraysize(writes1)); | |
5180 | |
5181 // Request |url_to_fetch0| to open connection to mail.example.org. | |
5182 HttpRequestInfo request0; | |
5183 request0.method = "GET"; | |
5184 request0.url = GURL(url_to_fetch0); | |
5185 request0.load_flags = 0; | |
5186 | |
5187 NetLogWithSource log; | |
5188 NormalSpdyTransactionHelper helper(request0, DEFAULT_PRIORITY, log, nullptr); | |
5189 helper.RunPreTestSetup(); | |
5190 | |
5191 // "spdy_pooling.pem" is valid for www.example.org, but not for | |
5192 // docs.example.org. | |
5193 std::unique_ptr<SSLSocketDataProvider> ssl_provider0( | |
5194 new SSLSocketDataProvider(ASYNC, OK)); | |
5195 ssl_provider0->cert = | |
5196 ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); | |
5197 helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0)); | |
5198 | |
5199 // "wildcard.pem" is valid for both www.example.org and docs.example.org. | |
5200 std::unique_ptr<SSLSocketDataProvider> ssl_provider1( | |
5201 new SSLSocketDataProvider(ASYNC, OK)); | |
5202 ssl_provider1->cert = | |
5203 ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"); | |
5204 helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1)); | |
5205 | |
5206 HttpNetworkTransaction* trans0 = helper.trans(); | |
5207 TestCompletionCallback callback0; | |
5208 int rv = trans0->Start(&request0, callback0.callback(), log); | |
5209 rv = callback0.GetResult(rv); | |
5210 EXPECT_THAT(rv, IsOk()); | |
5211 | |
5212 // Request |url_to_fetch1|, during which docs.example.org pushes | |
5213 // |url_to_push|, which happens to be for www.example.org, to which there is | |
5214 // already an open connection. | |
5215 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
5216 HttpRequestInfo request1; | |
5217 request1.method = "GET"; | |
5218 request1.url = GURL(url_to_fetch1); | |
5219 request1.load_flags = 0; | |
5220 TestCompletionCallback callback1; | |
5221 rv = trans1.Start(&request1, callback1.callback(), log); | |
5222 rv = callback1.GetResult(rv); | |
5223 EXPECT_THAT(rv, IsOk()); | |
5224 | |
5225 SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool(); | |
5226 HostPortPair host_port_pair0("mail.example.org", 443); | |
5227 SpdySessionKey key0(host_port_pair0, ProxyServer::Direct(), | |
5228 PRIVACY_MODE_DISABLED); | |
5229 base::WeakPtr<SpdySession> spdy_session0 = | |
5230 spdy_session_pool->FindAvailableSession( | |
5231 key0, GURL(), | |
5232 /* enable_ip_based_pooling = */ true, log); | |
5233 | |
5234 EXPECT_TRUE(spdy_session0->unclaimed_pushed_streams_.empty()); | |
5235 EXPECT_EQ(0u, spdy_session0->unclaimed_pushed_streams_.size()); | |
5236 | |
5237 HostPortPair host_port_pair1("docs.example.org", 443); | |
5238 SpdySessionKey key1(host_port_pair1, ProxyServer::Direct(), | |
5239 PRIVACY_MODE_DISABLED); | |
5240 base::WeakPtr<SpdySession> spdy_session1 = | |
5241 spdy_session_pool->FindAvailableSession( | |
5242 key1, GURL(), | |
5243 /* enable_ip_based_pooling = */ true, log); | |
5244 | |
5245 EXPECT_FALSE(spdy_session1->unclaimed_pushed_streams_.empty()); | |
5246 EXPECT_EQ(1u, spdy_session1->unclaimed_pushed_streams_.size()); | |
5247 EXPECT_EQ(1u, | |
5248 spdy_session1->unclaimed_pushed_streams_.count(GURL(url_to_push))); | |
5249 | |
5250 // Request |url_to_push|, which should be served from the pushed resource. | |
5251 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
5252 HttpRequestInfo push_request; | |
5253 push_request.method = "GET"; | |
5254 push_request.url = GURL(url_to_push); | |
5255 push_request.load_flags = 0; | |
5256 TestCompletionCallback callback2; | |
5257 rv = trans2.Start(&push_request, callback2.callback(), log); | |
5258 rv = callback2.GetResult(rv); | |
5259 EXPECT_THAT(rv, IsOk()); | |
5260 | |
5261 EXPECT_TRUE(spdy_session0->unclaimed_pushed_streams_.empty()); | |
5262 EXPECT_EQ(0u, spdy_session0->unclaimed_pushed_streams_.size()); | |
5263 | |
5264 EXPECT_TRUE(spdy_session1->unclaimed_pushed_streams_.empty()); | |
5265 EXPECT_EQ(0u, spdy_session1->unclaimed_pushed_streams_.size()); | |
5266 | |
5267 HttpResponseInfo response0 = *trans0->GetResponseInfo(); | |
5268 EXPECT_TRUE(response0.headers); | |
5269 EXPECT_EQ("HTTP/1.1 200", response0.headers->GetStatusLine()); | |
5270 | |
5271 SpdyString result0; | |
5272 ReadResult(trans0, &result0); | |
5273 EXPECT_EQ(kData0, result0); | |
5274 | |
5275 HttpResponseInfo response1 = *trans1.GetResponseInfo(); | |
5276 EXPECT_TRUE(response1.headers); | |
5277 EXPECT_EQ("HTTP/1.1 200", response1.headers->GetStatusLine()); | |
5278 | |
5279 SpdyString result1; | |
5280 ReadResult(&trans1, &result1); | |
5281 EXPECT_EQ(kData1, result1); | |
5282 | |
5283 HttpResponseInfo push_response = *trans2.GetResponseInfo(); | |
5284 EXPECT_TRUE(push_response.headers); | |
5285 EXPECT_EQ("HTTP/1.1 200", push_response.headers->GetStatusLine()); | |
5286 | |
5287 SpdyString result2; | |
5288 ReadResult(&trans2, &result2); | |
5289 EXPECT_EQ(kPushedData, result2); | |
5290 | |
5291 base::RunLoop().RunUntilIdle(); | |
5292 helper.VerifyDataConsumed(); | |
5293 VerifyStreamsClosed(helper); | |
5294 } | |
5295 | |
5296 TEST_F(SpdyNetworkTransactionTest, ServerPushInvalidCrossOrigin) { | |
5297 // "spdy_pooling.pem" is valid for www.example.org, | |
5298 // but not for invalid.example.org. | |
5299 const char* url_to_fetch = "https://www.example.org"; | |
5300 const char* url_to_push = "https://invalid.example.org"; | |
5301 | |
5302 SpdySerializedFrame headers( | |
5303 spdy_util_.ConstructSpdyGet(url_to_fetch, 1, LOWEST)); | |
5304 SpdySerializedFrame rst( | |
5305 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_REFUSED_STREAM)); | |
5306 MockWrite writes[] = { | |
5307 CreateMockWrite(headers, 0), CreateMockWrite(rst, 3), | |
5308 }; | |
5309 | |
5310 SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
5311 SpdySerializedFrame push( | |
5312 spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push)); | |
5313 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
5314 const char kPushedData[] = "pushed"; | |
5315 SpdySerializedFrame pushed_body(spdy_util_.ConstructSpdyDataFrame( | |
5316 2, kPushedData, strlen(kPushedData), true)); | |
5317 MockRead reads[] = { | |
5318 CreateMockRead(reply, 1), | |
5319 CreateMockRead(push, 2, SYNCHRONOUS), | |
5320 CreateMockRead(body, 4), | |
5321 CreateMockRead(pushed_body, 5, SYNCHRONOUS), | |
5322 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), | |
5323 }; | |
5324 | |
5325 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
5326 | |
5327 HttpRequestInfo request; | |
5328 request.method = "GET"; | |
5329 request.url = GURL(url_to_fetch); | |
5330 request.load_flags = 0; | |
5331 | |
5332 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
5333 NetLogWithSource(), nullptr); | |
5334 helper.RunToCompletion(&data); | |
5335 TransactionHelperResult out = helper.output(); | |
5336 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
5337 EXPECT_EQ("hello!", out.response_data); | |
5338 } | |
5339 | |
5340 TEST_F(SpdyNetworkTransactionTest, RetryAfterRefused) { | |
5341 // Construct the request. | |
5342 SpdySerializedFrame req( | |
5343 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
5344 // Will be destroyed by the RST before stream 3 starts. | |
5345 spdy_util_.UpdateWithStreamDestruction(1); | |
5346 SpdySerializedFrame req2( | |
5347 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
5348 MockWrite writes[] = { | |
5349 CreateMockWrite(req, 0), CreateMockWrite(req2, 2), | |
5350 }; | |
5351 | |
5352 SpdySerializedFrame refused( | |
5353 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_REFUSED_STREAM)); | |
5354 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
5355 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
5356 MockRead reads[] = { | |
5357 CreateMockRead(refused, 1), CreateMockRead(resp, 3), | |
5358 CreateMockRead(body, 4), MockRead(ASYNC, 0, 5) // EOF | |
5359 }; | |
5360 | |
5361 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
5362 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
5363 NetLogWithSource(), nullptr); | |
5364 | |
5365 helper.RunPreTestSetup(); | |
5366 helper.AddData(&data); | |
5367 | |
5368 HttpNetworkTransaction* trans = helper.trans(); | |
5369 | |
5370 // Start the transaction with basic parameters. | |
5371 TestCompletionCallback callback; | |
5372 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
5373 NetLogWithSource()); | |
5374 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
5375 rv = callback.WaitForResult(); | |
5376 EXPECT_THAT(rv, IsOk()); | |
5377 | |
5378 // Finish async network reads. | |
5379 base::RunLoop().RunUntilIdle(); | |
5380 | |
5381 // Verify that we consumed all test data. | |
5382 EXPECT_TRUE(data.AllReadDataConsumed()); | |
5383 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
5384 | |
5385 // Verify the response headers. | |
5386 HttpResponseInfo response = *trans->GetResponseInfo(); | |
5387 EXPECT_TRUE(response.headers); | |
5388 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
5389 } | |
5390 | |
5391 TEST_F(SpdyNetworkTransactionTest, OutOfOrderHeaders) { | |
5392 // This first request will start to establish the SpdySession. | |
5393 // Then we will start the second (MEDIUM priority) and then third | |
5394 // (HIGHEST priority) request in such a way that the third will actually | |
5395 // start before the second, causing the second to be numbered differently | |
5396 // than the order they were created. | |
5397 // | |
5398 // Note that the requests and responses created below are expectations | |
5399 // of what the above will produce on the wire, and hence are in the | |
5400 // initial->HIGHEST->LOWEST priority. | |
5401 // | |
5402 // Frames are created by SpdySession just before the write associated | |
5403 // with the frame is attempted, so stream dependencies will be based | |
5404 // on the streams alive at the point of the request write attempt. Thus | |
5405 // req1 is alive when req2 is attempted (during but not after the | |
5406 // |data.RunFor(2);| statement below) but not when req3 is attempted. | |
5407 // The call to spdy_util_.UpdateWithStreamDestruction() reflects this. | |
5408 SpdySerializedFrame req1( | |
5409 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
5410 SpdySerializedFrame req2( | |
5411 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, HIGHEST, true)); | |
5412 spdy_util_.UpdateWithStreamDestruction(1); | |
5413 SpdySerializedFrame req3( | |
5414 spdy_util_.ConstructSpdyGet(nullptr, 0, 5, MEDIUM, true)); | |
5415 MockWrite writes[] = { | |
5416 MockWrite(ASYNC, ERR_IO_PENDING, 0), CreateMockWrite(req1, 1), | |
5417 CreateMockWrite(req2, 5), CreateMockWrite(req3, 6), | |
5418 }; | |
5419 | |
5420 SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
5421 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
5422 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
5423 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
5424 SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5)); | |
5425 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, true)); | |
5426 MockRead reads[] = { | |
5427 CreateMockRead(resp1, 2), MockRead(ASYNC, ERR_IO_PENDING, 3), | |
5428 CreateMockRead(body1, 4), CreateMockRead(resp2, 7), | |
5429 CreateMockRead(body2, 8), CreateMockRead(resp3, 9), | |
5430 CreateMockRead(body3, 10), MockRead(ASYNC, 0, 11) // EOF | |
5431 }; | |
5432 | |
5433 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
5434 NormalSpdyTransactionHelper helper(CreateGetRequest(), LOWEST, | |
5435 NetLogWithSource(), nullptr); | |
5436 helper.RunPreTestSetup(); | |
5437 helper.AddData(&data); | |
5438 | |
5439 // Start the first transaction to set up the SpdySession | |
5440 HttpNetworkTransaction* trans = helper.trans(); | |
5441 TestCompletionCallback callback; | |
5442 HttpRequestInfo info1 = CreateGetRequest(); | |
5443 int rv = trans->Start(&info1, callback.callback(), NetLogWithSource()); | |
5444 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
5445 | |
5446 // Run the message loop, but do not allow the write to complete. | |
5447 // This leaves the SpdySession with a write pending, which prevents | |
5448 // SpdySession from attempting subsequent writes until this write completes. | |
5449 base::RunLoop().RunUntilIdle(); | |
5450 | |
5451 // Now, start both new transactions | |
5452 HttpRequestInfo info2 = CreateGetRequest(); | |
5453 TestCompletionCallback callback2; | |
5454 HttpNetworkTransaction trans2(MEDIUM, helper.session()); | |
5455 rv = trans2.Start(&info2, callback2.callback(), NetLogWithSource()); | |
5456 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
5457 base::RunLoop().RunUntilIdle(); | |
5458 | |
5459 HttpRequestInfo info3 = CreateGetRequest(); | |
5460 TestCompletionCallback callback3; | |
5461 HttpNetworkTransaction trans3(HIGHEST, helper.session()); | |
5462 rv = trans3.Start(&info3, callback3.callback(), NetLogWithSource()); | |
5463 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
5464 base::RunLoop().RunUntilIdle(); | |
5465 | |
5466 // We now have two HEADERS frames queued up which will be | |
5467 // dequeued only once the first write completes, which we | |
5468 // now allow to happen. | |
5469 ASSERT_TRUE(data.IsPaused()); | |
5470 data.Resume(); | |
5471 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
5472 | |
5473 // And now we can allow everything else to run to completion. | |
5474 data.Resume(); | |
5475 base::RunLoop().RunUntilIdle(); | |
5476 EXPECT_THAT(callback2.WaitForResult(), IsOk()); | |
5477 EXPECT_THAT(callback3.WaitForResult(), IsOk()); | |
5478 | |
5479 helper.VerifyDataConsumed(); | |
5480 | |
5481 // At this point the test is completed and we need to safely destroy | |
5482 // all allocated structures. Helper stores a transaction that has a | |
5483 // reference to a stack allocated request, which has a short lifetime, | |
5484 // and is accessed during the transaction destruction. We need to delete | |
5485 // the transaction while the request is still a valid object. | |
5486 helper.ResetTrans(); | |
5487 } | |
5488 | |
5489 // Test that sent data frames and received WINDOW_UPDATE frames change | |
5490 // the send_window_size_ correctly. | |
5491 | |
5492 // WINDOW_UPDATE is different than most other frames in that it can arrive | |
5493 // while the client is still sending the request body. In order to enforce | |
5494 // this scenario, we feed a couple of dummy frames and give a delay of 0 to | |
5495 // socket data provider, so that initial read that is done as soon as the | |
5496 // stream is created, succeeds and schedules another read. This way reads | |
5497 // and writes are interleaved; after doing a full frame write, SpdyStream | |
5498 // will break out of DoLoop and will read and process a WINDOW_UPDATE. | |
5499 // Once our WINDOW_UPDATE is read, we cannot send HEADERS right away | |
5500 // since request has not been completely written, therefore we feed | |
5501 // enough number of WINDOW_UPDATEs to finish the first read and cause a | |
5502 // write, leading to a complete write of request body; after that we send | |
5503 // a reply with a body, to cause a graceful shutdown. | |
5504 | |
5505 // TODO(agayev): develop a socket data provider where both, reads and | |
5506 // writes are ordered so that writing tests like these are easy and rewrite | |
5507 // all these tests using it. Right now we are working around the | |
5508 // limitations as described above and it's not deterministic, tests may | |
5509 // fail under specific circumstances. | |
5510 TEST_F(SpdyNetworkTransactionTest, WindowUpdateReceived) { | |
5511 static int kFrameCount = 2; | |
5512 std::unique_ptr<SpdyString> content( | |
5513 new SpdyString(kMaxSpdyFrameChunkSize, 'a')); | |
5514 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
5515 kDefaultUrl, 1, kMaxSpdyFrameChunkSize * kFrameCount, LOWEST, nullptr, | |
5516 0)); | |
5517 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame( | |
5518 1, content->c_str(), content->size(), false)); | |
5519 SpdySerializedFrame body_end(spdy_util_.ConstructSpdyDataFrame( | |
5520 1, content->c_str(), content->size(), true)); | |
5521 | |
5522 MockWrite writes[] = { | |
5523 CreateMockWrite(req, 0), CreateMockWrite(body, 1), | |
5524 CreateMockWrite(body_end, 2), | |
5525 }; | |
5526 | |
5527 static const int32_t kDeltaWindowSize = 0xff; | |
5528 static const int kDeltaCount = 4; | |
5529 SpdySerializedFrame window_update( | |
5530 spdy_util_.ConstructSpdyWindowUpdate(1, kDeltaWindowSize)); | |
5531 SpdySerializedFrame window_update_dummy( | |
5532 spdy_util_.ConstructSpdyWindowUpdate(2, kDeltaWindowSize)); | |
5533 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
5534 MockRead reads[] = { | |
5535 CreateMockRead(window_update_dummy, 3), | |
5536 CreateMockRead(window_update_dummy, 4), | |
5537 CreateMockRead(window_update_dummy, 5), | |
5538 CreateMockRead(window_update, 6), // Four updates, therefore window | |
5539 CreateMockRead(window_update, 7), // size should increase by | |
5540 CreateMockRead(window_update, 8), // kDeltaWindowSize * 4 | |
5541 CreateMockRead(window_update, 9), | |
5542 CreateMockRead(resp, 10), | |
5543 MockRead(ASYNC, ERR_IO_PENDING, 11), | |
5544 CreateMockRead(body_end, 12), | |
5545 MockRead(ASYNC, 0, 13) // EOF | |
5546 }; | |
5547 | |
5548 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
5549 | |
5550 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
5551 for (int i = 0; i < kFrameCount; ++i) { | |
5552 element_readers.push_back(base::WrapUnique( | |
5553 new UploadBytesElementReader(content->c_str(), content->size()))); | |
5554 } | |
5555 ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0); | |
5556 | |
5557 // Setup the request | |
5558 HttpRequestInfo request; | |
5559 request.method = "POST"; | |
5560 request.url = default_url_; | |
5561 request.upload_data_stream = &upload_data_stream; | |
5562 | |
5563 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
5564 NetLogWithSource(), nullptr); | |
5565 helper.AddData(&data); | |
5566 helper.RunPreTestSetup(); | |
5567 | |
5568 HttpNetworkTransaction* trans = helper.trans(); | |
5569 | |
5570 TestCompletionCallback callback; | |
5571 int rv = | |
5572 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
5573 | |
5574 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
5575 | |
5576 data.RunUntilPaused(); | |
5577 base::RunLoop().RunUntilIdle(); | |
5578 | |
5579 SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); | |
5580 ASSERT_TRUE(stream); | |
5581 ASSERT_TRUE(stream->stream()); | |
5582 EXPECT_EQ(static_cast<int>(kDefaultInitialWindowSize) + | |
5583 kDeltaWindowSize * kDeltaCount - | |
5584 kMaxSpdyFrameChunkSize * kFrameCount, | |
5585 stream->stream()->send_window_size()); | |
5586 | |
5587 data.Resume(); | |
5588 base::RunLoop().RunUntilIdle(); | |
5589 | |
5590 rv = callback.WaitForResult(); | |
5591 EXPECT_THAT(rv, IsOk()); | |
5592 | |
5593 helper.VerifyDataConsumed(); | |
5594 } | |
5595 | |
5596 // Test that received data frames and sent WINDOW_UPDATE frames change | |
5597 // the recv_window_size_ correctly. | |
5598 TEST_F(SpdyNetworkTransactionTest, WindowUpdateSent) { | |
5599 // Session level maximum window size that is more than twice the default | |
5600 // initial window size so that an initial window update is sent. | |
5601 const int32_t session_max_recv_window_size = 5 * 64 * 1024; | |
5602 ASSERT_LT(2 * kDefaultInitialWindowSize, session_max_recv_window_size); | |
5603 // Stream level maximum window size that is less than the session level | |
5604 // maximum window size so that we test for confusion between the two. | |
5605 const int32_t stream_max_recv_window_size = 4 * 64 * 1024; | |
5606 ASSERT_GT(session_max_recv_window_size, stream_max_recv_window_size); | |
5607 // Size of body to be sent. Has to be less than or equal to both window sizes | |
5608 // so that we do not run out of receiving window. Also has to be greater than | |
5609 // half of them so that it triggers both a session level and a stream level | |
5610 // window update frame. | |
5611 const int32_t kTargetSize = 3 * 64 * 1024; | |
5612 ASSERT_GE(session_max_recv_window_size, kTargetSize); | |
5613 ASSERT_GE(stream_max_recv_window_size, kTargetSize); | |
5614 ASSERT_LT(session_max_recv_window_size / 2, kTargetSize); | |
5615 ASSERT_LT(stream_max_recv_window_size / 2, kTargetSize); | |
5616 // Size of each DATA frame. | |
5617 const int32_t kChunkSize = 4096; | |
5618 // Size of window updates. | |
5619 ASSERT_EQ(0, session_max_recv_window_size / 2 % kChunkSize); | |
5620 const int32_t session_window_update_delta = | |
5621 session_max_recv_window_size / 2 + kChunkSize; | |
5622 ASSERT_EQ(0, stream_max_recv_window_size / 2 % kChunkSize); | |
5623 const int32_t stream_window_update_delta = | |
5624 stream_max_recv_window_size / 2 + kChunkSize; | |
5625 | |
5626 SettingsMap initial_settings; | |
5627 initial_settings[SETTINGS_HEADER_TABLE_SIZE] = kSpdyMaxHeaderTableSize; | |
5628 initial_settings[SETTINGS_MAX_CONCURRENT_STREAMS] = | |
5629 kSpdyMaxConcurrentPushedStreams; | |
5630 initial_settings[SETTINGS_INITIAL_WINDOW_SIZE] = stream_max_recv_window_size; | |
5631 SpdySerializedFrame initial_settings_frame( | |
5632 spdy_util_.ConstructSpdySettings(initial_settings)); | |
5633 SpdySerializedFrame initial_window_update( | |
5634 spdy_util_.ConstructSpdyWindowUpdate( | |
5635 kSessionFlowControlStreamId, | |
5636 session_max_recv_window_size - kDefaultInitialWindowSize)); | |
5637 SpdySerializedFrame req( | |
5638 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
5639 SpdySerializedFrame session_window_update( | |
5640 spdy_util_.ConstructSpdyWindowUpdate(0, session_window_update_delta)); | |
5641 SpdySerializedFrame stream_window_update( | |
5642 spdy_util_.ConstructSpdyWindowUpdate(1, stream_window_update_delta)); | |
5643 | |
5644 std::vector<MockWrite> writes; | |
5645 writes.push_back(MockWrite(ASYNC, kHttp2ConnectionHeaderPrefix, | |
5646 kHttp2ConnectionHeaderPrefixSize, 0)); | |
5647 writes.push_back(CreateMockWrite(initial_settings_frame, writes.size())); | |
5648 writes.push_back(CreateMockWrite(initial_window_update, writes.size())); | |
5649 writes.push_back(CreateMockWrite(req, writes.size())); | |
5650 | |
5651 std::vector<MockRead> reads; | |
5652 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
5653 reads.push_back(CreateMockRead(resp, writes.size() + reads.size())); | |
5654 | |
5655 std::vector<SpdySerializedFrame> body_frames; | |
5656 const SpdyString body_data(kChunkSize, 'x'); | |
5657 for (size_t remaining = kTargetSize; remaining != 0;) { | |
5658 size_t frame_size = std::min(remaining, body_data.size()); | |
5659 body_frames.push_back(spdy_util_.ConstructSpdyDataFrame(1, body_data.data(), | |
5660 frame_size, false)); | |
5661 reads.push_back( | |
5662 CreateMockRead(body_frames.back(), writes.size() + reads.size())); | |
5663 remaining -= frame_size; | |
5664 } | |
5665 // Yield. | |
5666 reads.push_back( | |
5667 MockRead(SYNCHRONOUS, ERR_IO_PENDING, writes.size() + reads.size())); | |
5668 | |
5669 writes.push_back( | |
5670 CreateMockWrite(session_window_update, writes.size() + reads.size())); | |
5671 writes.push_back( | |
5672 CreateMockWrite(stream_window_update, writes.size() + reads.size())); | |
5673 | |
5674 SequencedSocketData data(reads.data(), reads.size(), writes.data(), | |
5675 writes.size()); | |
5676 | |
5677 auto session_deps = base::MakeUnique<SpdySessionDependencies>(); | |
5678 session_deps->session_max_recv_window_size = session_max_recv_window_size; | |
5679 session_deps->http2_settings[SETTINGS_INITIAL_WINDOW_SIZE] = | |
5680 stream_max_recv_window_size; | |
5681 | |
5682 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
5683 NetLogWithSource(), | |
5684 std::move(session_deps)); | |
5685 helper.AddData(&data); | |
5686 helper.RunPreTestSetup(); | |
5687 | |
5688 SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool(); | |
5689 SpdySessionPoolPeer pool_peer(spdy_session_pool); | |
5690 pool_peer.SetEnableSendingInitialData(true); | |
5691 | |
5692 HttpNetworkTransaction* trans = helper.trans(); | |
5693 TestCompletionCallback callback; | |
5694 int rv = | |
5695 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
5696 | |
5697 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
5698 rv = callback.WaitForResult(); | |
5699 EXPECT_THAT(rv, IsOk()); | |
5700 | |
5701 // Finish async network reads. | |
5702 base::RunLoop().RunUntilIdle(); | |
5703 | |
5704 SpdyHttpStream* stream = | |
5705 static_cast<SpdyHttpStream*>(trans->stream_.get()); | |
5706 ASSERT_TRUE(stream); | |
5707 ASSERT_TRUE(stream->stream()); | |
5708 | |
5709 // All data has been read, but not consumed. The window reflects this. | |
5710 EXPECT_EQ(static_cast<int>(stream_max_recv_window_size - kTargetSize), | |
5711 stream->stream()->recv_window_size()); | |
5712 | |
5713 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
5714 ASSERT_TRUE(response); | |
5715 ASSERT_TRUE(response->headers); | |
5716 EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); | |
5717 EXPECT_TRUE(response->was_fetched_via_spdy); | |
5718 | |
5719 // Issue a read which will cause a WINDOW_UPDATE to be sent and window | |
5720 // size increased to default. | |
5721 scoped_refptr<IOBuffer> buf(new IOBuffer(kTargetSize)); | |
5722 EXPECT_EQ(static_cast<int>(kTargetSize), | |
5723 trans->Read(buf.get(), kTargetSize, CompletionCallback())); | |
5724 EXPECT_EQ(static_cast<int>(stream_max_recv_window_size), | |
5725 stream->stream()->recv_window_size()); | |
5726 EXPECT_THAT(SpdyStringPiece(buf->data(), kTargetSize), Each(Eq('x'))); | |
5727 | |
5728 // Allow scheduled WINDOW_UPDATE frames to write. | |
5729 base::RunLoop().RunUntilIdle(); | |
5730 helper.VerifyDataConsumed(); | |
5731 } | |
5732 | |
5733 // Test that WINDOW_UPDATE frame causing overflow is handled correctly. | |
5734 TEST_F(SpdyNetworkTransactionTest, WindowUpdateOverflow) { | |
5735 // Number of full frames we hope to write (but will not, used to | |
5736 // set content-length header correctly) | |
5737 static int kFrameCount = 3; | |
5738 | |
5739 std::unique_ptr<SpdyString> content( | |
5740 new SpdyString(kMaxSpdyFrameChunkSize, 'a')); | |
5741 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
5742 kDefaultUrl, 1, kMaxSpdyFrameChunkSize * kFrameCount, LOWEST, nullptr, | |
5743 0)); | |
5744 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame( | |
5745 1, content->c_str(), content->size(), false)); | |
5746 SpdySerializedFrame rst( | |
5747 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_FLOW_CONTROL_ERROR)); | |
5748 | |
5749 // We're not going to write a data frame with FIN, we'll receive a bad | |
5750 // WINDOW_UPDATE while sending a request and will send a RST_STREAM frame. | |
5751 MockWrite writes[] = { | |
5752 CreateMockWrite(req, 0), CreateMockWrite(body, 2), | |
5753 CreateMockWrite(rst, 3), | |
5754 }; | |
5755 | |
5756 static const int32_t kDeltaWindowSize = 0x7fffffff; // cause an overflow | |
5757 SpdySerializedFrame window_update( | |
5758 spdy_util_.ConstructSpdyWindowUpdate(1, kDeltaWindowSize)); | |
5759 MockRead reads[] = { | |
5760 CreateMockRead(window_update, 1), MockRead(ASYNC, 0, 4) // EOF | |
5761 }; | |
5762 | |
5763 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
5764 | |
5765 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
5766 for (int i = 0; i < kFrameCount; ++i) { | |
5767 element_readers.push_back(base::WrapUnique( | |
5768 new UploadBytesElementReader(content->c_str(), content->size()))); | |
5769 } | |
5770 ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0); | |
5771 | |
5772 // Setup the request | |
5773 HttpRequestInfo request; | |
5774 request.method = "POST"; | |
5775 request.url = default_url_; | |
5776 request.upload_data_stream = &upload_data_stream; | |
5777 | |
5778 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
5779 NetLogWithSource(), nullptr); | |
5780 helper.RunPreTestSetup(); | |
5781 helper.AddData(&data); | |
5782 HttpNetworkTransaction* trans = helper.trans(); | |
5783 | |
5784 TestCompletionCallback callback; | |
5785 int rv = | |
5786 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
5787 ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); | |
5788 | |
5789 base::RunLoop().RunUntilIdle(); | |
5790 ASSERT_TRUE(callback.have_result()); | |
5791 EXPECT_THAT(callback.WaitForResult(), IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
5792 helper.VerifyDataConsumed(); | |
5793 } | |
5794 | |
5795 // Test that after hitting a send window size of 0, the write process | |
5796 // stalls and upon receiving WINDOW_UPDATE frame write resumes. | |
5797 | |
5798 // This test constructs a POST request followed by enough data frames | |
5799 // containing 'a' that would make the window size 0, followed by another | |
5800 // data frame containing default content (which is "hello!") and this frame | |
5801 // also contains a FIN flag. SequencedSocketData is used to enforce all | |
5802 // writes, save the last, go through before a read could happen. The last frame | |
5803 // ("hello!") is not permitted to go through since by the time its turn | |
5804 // arrives, window size is 0. At this point MessageLoop::Run() called via | |
5805 // callback would block. Therefore we call MessageLoop::RunUntilIdle() | |
5806 // which returns after performing all possible writes. We use DCHECKS to | |
5807 // ensure that last data frame is still there and stream has stalled. | |
5808 // After that, next read is artifically enforced, which causes a | |
5809 // WINDOW_UPDATE to be read and I/O process resumes. | |
5810 TEST_F(SpdyNetworkTransactionTest, FlowControlStallResume) { | |
5811 const int32_t initial_window_size = kDefaultInitialWindowSize; | |
5812 // Number of upload data buffers we need to send to zero out the window size | |
5813 // is the minimal number of upload buffers takes to be bigger than | |
5814 // |initial_window_size|. | |
5815 size_t num_upload_buffers = | |
5816 ceil(static_cast<double>(initial_window_size) / kBufferSize); | |
5817 // Each upload data buffer consists of |num_frames_in_one_upload_buffer| | |
5818 // frames, each with |kMaxSpdyFrameChunkSize| bytes except the last frame, | |
5819 // which has kBufferSize % kMaxSpdyChunkSize bytes. | |
5820 size_t num_frames_in_one_upload_buffer = | |
5821 ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize); | |
5822 | |
5823 // Construct content for a data frame of maximum size. | |
5824 SpdyString content(kMaxSpdyFrameChunkSize, 'a'); | |
5825 | |
5826 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
5827 kDefaultUrl, 1, | |
5828 /*content_length=*/kBufferSize * num_upload_buffers + kUploadDataSize, | |
5829 LOWEST, nullptr, 0)); | |
5830 | |
5831 // Full frames. | |
5832 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame( | |
5833 1, content.c_str(), content.size(), false)); | |
5834 | |
5835 // Last frame in each upload data buffer. | |
5836 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame( | |
5837 1, content.c_str(), kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
5838 | |
5839 // The very last frame before the stalled frames. | |
5840 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame( | |
5841 1, content.c_str(), | |
5842 initial_window_size % kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
5843 | |
5844 // Data frames to be sent once WINDOW_UPDATE frame is received. | |
5845 | |
5846 // If kBufferSize * num_upload_buffers > initial_window_size, | |
5847 // we need one additional frame to send the rest of 'a'. | |
5848 SpdyString last_body(kBufferSize * num_upload_buffers - initial_window_size, | |
5849 'a'); | |
5850 SpdySerializedFrame body4(spdy_util_.ConstructSpdyDataFrame( | |
5851 1, last_body.c_str(), last_body.size(), false)); | |
5852 | |
5853 // Also send a "hello!" after WINDOW_UPDATE. | |
5854 SpdySerializedFrame body5(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
5855 | |
5856 // Fill in mock writes. | |
5857 size_t i = 0; | |
5858 std::vector<MockWrite> writes; | |
5859 writes.push_back(CreateMockWrite(req, i++)); | |
5860 for (size_t j = 0; j < num_upload_buffers; j++) { | |
5861 for (size_t k = 0; k < num_frames_in_one_upload_buffer; k++) { | |
5862 if (k == num_frames_in_one_upload_buffer - 1 && | |
5863 kBufferSize % kMaxSpdyFrameChunkSize != 0) { | |
5864 if (j == num_upload_buffers - 1 && | |
5865 (initial_window_size % kBufferSize != 0)) { | |
5866 writes.push_back(CreateMockWrite(body3, i++)); | |
5867 } else { | |
5868 writes.push_back(CreateMockWrite(body2, i++)); | |
5869 } | |
5870 } else { | |
5871 writes.push_back(CreateMockWrite(body1, i++)); | |
5872 } | |
5873 } | |
5874 } | |
5875 | |
5876 // Fill in mock reads. | |
5877 std::vector<MockRead> reads; | |
5878 // Force a pause. | |
5879 reads.push_back(MockRead(ASYNC, ERR_IO_PENDING, i++)); | |
5880 // Construct read frame for window updates that gives enough space to upload | |
5881 // the rest of the data. | |
5882 SpdySerializedFrame session_window_update( | |
5883 spdy_util_.ConstructSpdyWindowUpdate(0, | |
5884 kUploadDataSize + last_body.size())); | |
5885 SpdySerializedFrame window_update(spdy_util_.ConstructSpdyWindowUpdate( | |
5886 1, kUploadDataSize + last_body.size())); | |
5887 | |
5888 reads.push_back(CreateMockRead(session_window_update, i++)); | |
5889 reads.push_back(CreateMockRead(window_update, i++)); | |
5890 | |
5891 // Stalled frames which can be sent after receiving window updates. | |
5892 if (last_body.size() > 0) | |
5893 writes.push_back(CreateMockWrite(body4, i++)); | |
5894 writes.push_back(CreateMockWrite(body5, i++)); | |
5895 | |
5896 SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
5897 reads.push_back(CreateMockRead(reply, i++)); | |
5898 reads.push_back(CreateMockRead(body2, i++)); | |
5899 reads.push_back(CreateMockRead(body5, i++)); | |
5900 reads.push_back(MockRead(ASYNC, 0, i++)); // EOF | |
5901 | |
5902 SequencedSocketData data(reads.data(), reads.size(), writes.data(), | |
5903 writes.size()); | |
5904 | |
5905 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
5906 SpdyString upload_data_string(kBufferSize * num_upload_buffers, 'a'); | |
5907 upload_data_string.append(kUploadData, kUploadDataSize); | |
5908 element_readers.push_back(base::WrapUnique(new UploadBytesElementReader( | |
5909 upload_data_string.c_str(), upload_data_string.size()))); | |
5910 ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0); | |
5911 | |
5912 HttpRequestInfo request; | |
5913 request.method = "POST"; | |
5914 request.url = default_url_; | |
5915 request.upload_data_stream = &upload_data_stream; | |
5916 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
5917 NetLogWithSource(), nullptr); | |
5918 helper.AddData(&data); | |
5919 helper.RunPreTestSetup(); | |
5920 | |
5921 HttpNetworkTransaction* trans = helper.trans(); | |
5922 | |
5923 TestCompletionCallback callback; | |
5924 int rv = | |
5925 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
5926 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
5927 | |
5928 base::RunLoop().RunUntilIdle(); // Write as much as we can. | |
5929 | |
5930 SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); | |
5931 ASSERT_TRUE(stream); | |
5932 ASSERT_TRUE(stream->stream()); | |
5933 EXPECT_EQ(0, stream->stream()->send_window_size()); | |
5934 if (initial_window_size % kBufferSize != 0) { | |
5935 // If it does not take whole number of full upload buffer to zero out | |
5936 // initial window size, then the upload data is not at EOF, because the | |
5937 // last read must be stalled. | |
5938 EXPECT_FALSE(upload_data_stream.IsEOF()); | |
5939 } else { | |
5940 // All the body data should have been read. | |
5941 // TODO(satorux): This is because of the weirdness in reading the request | |
5942 // body in OnSendBodyComplete(). See crbug.com/113107. | |
5943 EXPECT_TRUE(upload_data_stream.IsEOF()); | |
5944 } | |
5945 // But the body is not yet fully sent (kUploadData is not yet sent) | |
5946 // since we're send-stalled. | |
5947 EXPECT_TRUE(stream->stream()->send_stalled_by_flow_control()); | |
5948 | |
5949 data.Resume(); // Read in WINDOW_UPDATE frame. | |
5950 rv = callback.WaitForResult(); | |
5951 EXPECT_THAT(rv, IsOk()); | |
5952 | |
5953 // Finish async network reads. | |
5954 base::RunLoop().RunUntilIdle(); | |
5955 helper.VerifyDataConsumed(); | |
5956 } | |
5957 | |
5958 // Test we correctly handle the case where the SETTINGS frame results in | |
5959 // unstalling the send window. | |
5960 TEST_F(SpdyNetworkTransactionTest, FlowControlStallResumeAfterSettings) { | |
5961 const int32_t initial_window_size = kDefaultInitialWindowSize; | |
5962 // Number of upload data buffers we need to send to zero out the window size | |
5963 // is the minimal number of upload buffers takes to be bigger than | |
5964 // |initial_window_size|. | |
5965 size_t num_upload_buffers = | |
5966 ceil(static_cast<double>(initial_window_size) / kBufferSize); | |
5967 // Each upload data buffer consists of |num_frames_in_one_upload_buffer| | |
5968 // frames, each with |kMaxSpdyFrameChunkSize| bytes except the last frame, | |
5969 // which has kBufferSize % kMaxSpdyChunkSize bytes. | |
5970 size_t num_frames_in_one_upload_buffer = | |
5971 ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize); | |
5972 | |
5973 // Construct content for a data frame of maximum size. | |
5974 SpdyString content(kMaxSpdyFrameChunkSize, 'a'); | |
5975 | |
5976 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
5977 kDefaultUrl, 1, | |
5978 /*content_length=*/kBufferSize * num_upload_buffers + kUploadDataSize, | |
5979 LOWEST, nullptr, 0)); | |
5980 | |
5981 // Full frames. | |
5982 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame( | |
5983 1, content.c_str(), content.size(), false)); | |
5984 | |
5985 // Last frame in each upload data buffer. | |
5986 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame( | |
5987 1, content.c_str(), kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
5988 | |
5989 // The very last frame before the stalled frames. | |
5990 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame( | |
5991 1, content.c_str(), | |
5992 initial_window_size % kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
5993 | |
5994 // Data frames to be sent once WINDOW_UPDATE frame is received. | |
5995 | |
5996 // If kBufferSize * num_upload_buffers > initial_window_size, | |
5997 // we need one additional frame to send the rest of 'a'. | |
5998 SpdyString last_body(kBufferSize * num_upload_buffers - initial_window_size, | |
5999 'a'); | |
6000 SpdySerializedFrame body4(spdy_util_.ConstructSpdyDataFrame( | |
6001 1, last_body.c_str(), last_body.size(), false)); | |
6002 | |
6003 // Also send a "hello!" after WINDOW_UPDATE. | |
6004 SpdySerializedFrame body5(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
6005 | |
6006 // Fill in mock writes. | |
6007 size_t i = 0; | |
6008 std::vector<MockWrite> writes; | |
6009 writes.push_back(CreateMockWrite(req, i++)); | |
6010 for (size_t j = 0; j < num_upload_buffers; j++) { | |
6011 for (size_t k = 0; k < num_frames_in_one_upload_buffer; k++) { | |
6012 if (k == num_frames_in_one_upload_buffer - 1 && | |
6013 kBufferSize % kMaxSpdyFrameChunkSize != 0) { | |
6014 if (j == num_upload_buffers - 1 && | |
6015 (initial_window_size % kBufferSize != 0)) { | |
6016 writes.push_back(CreateMockWrite(body3, i++)); | |
6017 } else { | |
6018 writes.push_back(CreateMockWrite(body2, i++)); | |
6019 } | |
6020 } else { | |
6021 writes.push_back(CreateMockWrite(body1, i++)); | |
6022 } | |
6023 } | |
6024 } | |
6025 | |
6026 // Fill in mock reads. | |
6027 std::vector<MockRead> reads; | |
6028 // Force a pause. | |
6029 reads.push_back(MockRead(ASYNC, ERR_IO_PENDING, i++)); | |
6030 | |
6031 // Construct read frame for SETTINGS that gives enough space to upload the | |
6032 // rest of the data. | |
6033 SettingsMap settings; | |
6034 settings[SETTINGS_INITIAL_WINDOW_SIZE] = initial_window_size * 2; | |
6035 SpdySerializedFrame settings_frame_large( | |
6036 spdy_util_.ConstructSpdySettings(settings)); | |
6037 | |
6038 reads.push_back(CreateMockRead(settings_frame_large, i++)); | |
6039 | |
6040 SpdySerializedFrame session_window_update( | |
6041 spdy_util_.ConstructSpdyWindowUpdate(0, | |
6042 last_body.size() + kUploadDataSize)); | |
6043 reads.push_back(CreateMockRead(session_window_update, i++)); | |
6044 | |
6045 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
6046 writes.push_back(CreateMockWrite(settings_ack, i++)); | |
6047 | |
6048 // Stalled frames which can be sent after |settings_ack|. | |
6049 if (last_body.size() > 0) | |
6050 writes.push_back(CreateMockWrite(body4, i++)); | |
6051 writes.push_back(CreateMockWrite(body5, i++)); | |
6052 | |
6053 SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
6054 reads.push_back(CreateMockRead(reply, i++)); | |
6055 reads.push_back(CreateMockRead(body2, i++)); | |
6056 reads.push_back(CreateMockRead(body5, i++)); | |
6057 reads.push_back(MockRead(ASYNC, 0, i++)); // EOF | |
6058 | |
6059 // Force all writes to happen before any read, last write will not | |
6060 // actually queue a frame, due to window size being 0. | |
6061 SequencedSocketData data(reads.data(), reads.size(), writes.data(), | |
6062 writes.size()); | |
6063 | |
6064 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
6065 SpdyString upload_data_string(kBufferSize * num_upload_buffers, 'a'); | |
6066 upload_data_string.append(kUploadData, kUploadDataSize); | |
6067 element_readers.push_back(base::WrapUnique(new UploadBytesElementReader( | |
6068 upload_data_string.c_str(), upload_data_string.size()))); | |
6069 ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0); | |
6070 | |
6071 HttpRequestInfo request; | |
6072 request.method = "POST"; | |
6073 request.url = default_url_; | |
6074 request.upload_data_stream = &upload_data_stream; | |
6075 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
6076 NetLogWithSource(), nullptr); | |
6077 helper.RunPreTestSetup(); | |
6078 helper.AddData(&data); | |
6079 | |
6080 HttpNetworkTransaction* trans = helper.trans(); | |
6081 | |
6082 TestCompletionCallback callback; | |
6083 int rv = | |
6084 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
6085 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
6086 | |
6087 data.RunUntilPaused(); // Write as much as we can. | |
6088 base::RunLoop().RunUntilIdle(); | |
6089 | |
6090 SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); | |
6091 ASSERT_TRUE(stream); | |
6092 ASSERT_TRUE(stream->stream()); | |
6093 EXPECT_EQ(0, stream->stream()->send_window_size()); | |
6094 | |
6095 if (initial_window_size % kBufferSize != 0) { | |
6096 // If it does not take whole number of full upload buffer to zero out | |
6097 // initial window size, then the upload data is not at EOF, because the | |
6098 // last read must be stalled. | |
6099 EXPECT_FALSE(upload_data_stream.IsEOF()); | |
6100 } else { | |
6101 // All the body data should have been read. | |
6102 // TODO(satorux): This is because of the weirdness in reading the request | |
6103 // body in OnSendBodyComplete(). See crbug.com/113107. | |
6104 EXPECT_TRUE(upload_data_stream.IsEOF()); | |
6105 } | |
6106 // But the body is not yet fully sent (kUploadData is not yet sent) | |
6107 // since we're send-stalled. | |
6108 EXPECT_TRUE(stream->stream()->send_stalled_by_flow_control()); | |
6109 | |
6110 // Read in SETTINGS frame to unstall. | |
6111 data.Resume(); | |
6112 base::RunLoop().RunUntilIdle(); | |
6113 | |
6114 rv = callback.WaitForResult(); | |
6115 helper.VerifyDataConsumed(); | |
6116 // If stream is nullptr, that means it was unstalled and closed. | |
6117 EXPECT_TRUE(stream->stream() == nullptr); | |
6118 } | |
6119 | |
6120 // Test we correctly handle the case where the SETTINGS frame results in a | |
6121 // negative send window size. | |
6122 TEST_F(SpdyNetworkTransactionTest, FlowControlNegativeSendWindowSize) { | |
6123 const int32_t initial_window_size = kDefaultInitialWindowSize; | |
6124 // Number of upload data buffers we need to send to zero out the window size | |
6125 // is the minimal number of upload buffers takes to be bigger than | |
6126 // |initial_window_size|. | |
6127 size_t num_upload_buffers = | |
6128 ceil(static_cast<double>(initial_window_size) / kBufferSize); | |
6129 // Each upload data buffer consists of |num_frames_in_one_upload_buffer| | |
6130 // frames, each with |kMaxSpdyFrameChunkSize| bytes except the last frame, | |
6131 // which has kBufferSize % kMaxSpdyChunkSize bytes. | |
6132 size_t num_frames_in_one_upload_buffer = | |
6133 ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize); | |
6134 | |
6135 // Construct content for a data frame of maximum size. | |
6136 SpdyString content(kMaxSpdyFrameChunkSize, 'a'); | |
6137 | |
6138 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
6139 kDefaultUrl, 1, | |
6140 /*content_length=*/kBufferSize * num_upload_buffers + kUploadDataSize, | |
6141 LOWEST, nullptr, 0)); | |
6142 | |
6143 // Full frames. | |
6144 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame( | |
6145 1, content.c_str(), content.size(), false)); | |
6146 | |
6147 // Last frame in each upload data buffer. | |
6148 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame( | |
6149 1, content.c_str(), kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
6150 | |
6151 // The very last frame before the stalled frames. | |
6152 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame( | |
6153 1, content.c_str(), | |
6154 initial_window_size % kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
6155 | |
6156 // Data frames to be sent once WINDOW_UPDATE frame is received. | |
6157 | |
6158 // If kBufferSize * num_upload_buffers > initial_window_size, | |
6159 // we need one additional frame to send the rest of 'a'. | |
6160 SpdyString last_body(kBufferSize * num_upload_buffers - initial_window_size, | |
6161 'a'); | |
6162 SpdySerializedFrame body4(spdy_util_.ConstructSpdyDataFrame( | |
6163 1, last_body.c_str(), last_body.size(), false)); | |
6164 | |
6165 // Also send a "hello!" after WINDOW_UPDATE. | |
6166 SpdySerializedFrame body5(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
6167 | |
6168 // Fill in mock writes. | |
6169 size_t i = 0; | |
6170 std::vector<MockWrite> writes; | |
6171 writes.push_back(CreateMockWrite(req, i++)); | |
6172 for (size_t j = 0; j < num_upload_buffers; j++) { | |
6173 for (size_t k = 0; k < num_frames_in_one_upload_buffer; k++) { | |
6174 if (k == num_frames_in_one_upload_buffer - 1 && | |
6175 kBufferSize % kMaxSpdyFrameChunkSize != 0) { | |
6176 if (j == num_upload_buffers - 1 && | |
6177 (initial_window_size % kBufferSize != 0)) { | |
6178 writes.push_back(CreateMockWrite(body3, i++)); | |
6179 } else { | |
6180 writes.push_back(CreateMockWrite(body2, i++)); | |
6181 } | |
6182 } else { | |
6183 writes.push_back(CreateMockWrite(body1, i++)); | |
6184 } | |
6185 } | |
6186 } | |
6187 | |
6188 // Fill in mock reads. | |
6189 std::vector<MockRead> reads; | |
6190 // Force a pause. | |
6191 reads.push_back(MockRead(ASYNC, ERR_IO_PENDING, i++)); | |
6192 // Construct read frame for SETTINGS that makes the send_window_size | |
6193 // negative. | |
6194 SettingsMap new_settings; | |
6195 new_settings[SETTINGS_INITIAL_WINDOW_SIZE] = initial_window_size / 2; | |
6196 SpdySerializedFrame settings_frame_small( | |
6197 spdy_util_.ConstructSpdySettings(new_settings)); | |
6198 // Construct read frames for WINDOW_UPDATE that makes the send_window_size | |
6199 // positive. | |
6200 SpdySerializedFrame session_window_update_init_size( | |
6201 spdy_util_.ConstructSpdyWindowUpdate(0, initial_window_size)); | |
6202 SpdySerializedFrame window_update_init_size( | |
6203 spdy_util_.ConstructSpdyWindowUpdate(1, initial_window_size)); | |
6204 | |
6205 reads.push_back(CreateMockRead(settings_frame_small, i++)); | |
6206 reads.push_back(CreateMockRead(session_window_update_init_size, i++)); | |
6207 reads.push_back(CreateMockRead(window_update_init_size, i++)); | |
6208 | |
6209 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
6210 writes.push_back(CreateMockWrite(settings_ack, i++)); | |
6211 | |
6212 // Stalled frames which can be sent after |settings_ack|. | |
6213 if (last_body.size() > 0) | |
6214 writes.push_back(CreateMockWrite(body4, i++)); | |
6215 writes.push_back(CreateMockWrite(body5, i++)); | |
6216 | |
6217 SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
6218 reads.push_back(CreateMockRead(reply, i++)); | |
6219 reads.push_back(CreateMockRead(body2, i++)); | |
6220 reads.push_back(CreateMockRead(body5, i++)); | |
6221 reads.push_back(MockRead(ASYNC, 0, i++)); // EOF | |
6222 | |
6223 // Force all writes to happen before any read, last write will not | |
6224 // actually queue a frame, due to window size being 0. | |
6225 SequencedSocketData data(reads.data(), reads.size(), writes.data(), | |
6226 writes.size()); | |
6227 | |
6228 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
6229 SpdyString upload_data_string(kBufferSize * num_upload_buffers, 'a'); | |
6230 upload_data_string.append(kUploadData, kUploadDataSize); | |
6231 element_readers.push_back(base::WrapUnique(new UploadBytesElementReader( | |
6232 upload_data_string.c_str(), upload_data_string.size()))); | |
6233 ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0); | |
6234 | |
6235 HttpRequestInfo request; | |
6236 request.method = "POST"; | |
6237 request.url = default_url_; | |
6238 request.upload_data_stream = &upload_data_stream; | |
6239 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
6240 NetLogWithSource(), nullptr); | |
6241 helper.RunPreTestSetup(); | |
6242 helper.AddData(&data); | |
6243 | |
6244 HttpNetworkTransaction* trans = helper.trans(); | |
6245 | |
6246 TestCompletionCallback callback; | |
6247 int rv = | |
6248 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
6249 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
6250 | |
6251 data.RunUntilPaused(); // Write as much as we can. | |
6252 base::RunLoop().RunUntilIdle(); | |
6253 | |
6254 SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); | |
6255 ASSERT_TRUE(stream); | |
6256 ASSERT_TRUE(stream->stream()); | |
6257 EXPECT_EQ(0, stream->stream()->send_window_size()); | |
6258 | |
6259 if (initial_window_size % kBufferSize != 0) { | |
6260 // If it does not take whole number of full upload buffer to zero out | |
6261 // initial window size, then the upload data is not at EOF, because the | |
6262 // last read must be stalled. | |
6263 EXPECT_FALSE(upload_data_stream.IsEOF()); | |
6264 } else { | |
6265 // All the body data should have been read. | |
6266 // TODO(satorux): This is because of the weirdness in reading the request | |
6267 // body in OnSendBodyComplete(). See crbug.com/113107. | |
6268 EXPECT_TRUE(upload_data_stream.IsEOF()); | |
6269 } | |
6270 | |
6271 // Read in WINDOW_UPDATE or SETTINGS frame. | |
6272 data.Resume(); | |
6273 base::RunLoop().RunUntilIdle(); | |
6274 rv = callback.WaitForResult(); | |
6275 helper.VerifyDataConsumed(); | |
6276 } | |
6277 | |
6278 TEST_F(SpdyNetworkTransactionTest, GoAwayOnOddPushStreamId) { | |
6279 SpdyHeaderBlock push_headers; | |
6280 spdy_util_.AddUrlToHeaderBlock("http://www.example.org/a.dat", &push_headers); | |
6281 SpdySerializedFrame push( | |
6282 spdy_util_.ConstructInitialSpdyPushFrame(std::move(push_headers), 3, 1)); | |
6283 MockRead reads[] = {CreateMockRead(push, 1)}; | |
6284 | |
6285 SpdySerializedFrame req( | |
6286 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
6287 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
6288 0, ERROR_CODE_PROTOCOL_ERROR, "Odd push stream id.")); | |
6289 MockWrite writes[] = { | |
6290 CreateMockWrite(req, 0), CreateMockWrite(goaway, 2), | |
6291 }; | |
6292 | |
6293 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
6294 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
6295 NetLogWithSource(), nullptr); | |
6296 helper.RunToCompletion(&data); | |
6297 TransactionHelperResult out = helper.output(); | |
6298 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
6299 } | |
6300 | |
6301 TEST_F(SpdyNetworkTransactionTest, | |
6302 GoAwayOnPushStreamIdLesserOrEqualThanLastAccepted) { | |
6303 SpdySerializedFrame push_a(spdy_util_.ConstructSpdyPush( | |
6304 nullptr, 0, 4, 1, GetDefaultUrlWithPath("/a.dat").c_str())); | |
6305 SpdyHeaderBlock push_b_headers; | |
6306 spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/b.dat"), | |
6307 &push_b_headers); | |
6308 SpdySerializedFrame push_b(spdy_util_.ConstructInitialSpdyPushFrame( | |
6309 std::move(push_b_headers), 2, 1)); | |
6310 MockRead reads[] = { | |
6311 CreateMockRead(push_a, 1), CreateMockRead(push_b, 3), | |
6312 }; | |
6313 | |
6314 SpdySerializedFrame req( | |
6315 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
6316 SpdySerializedFrame priority_a( | |
6317 spdy_util_.ConstructSpdyPriority(4, 1, IDLE, true)); | |
6318 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
6319 4, ERROR_CODE_PROTOCOL_ERROR, | |
6320 "New push stream id must be greater than the last accepted.")); | |
6321 MockWrite writes[] = { | |
6322 CreateMockWrite(req, 0), CreateMockWrite(priority_a, 2), | |
6323 CreateMockWrite(goaway, 4), | |
6324 }; | |
6325 | |
6326 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
6327 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
6328 NetLogWithSource(), nullptr); | |
6329 helper.RunToCompletion(&data); | |
6330 TransactionHelperResult out = helper.output(); | |
6331 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
6332 } | |
6333 | |
6334 // Regression test for https://crbug.com/493348: request header exceeds 16 kB | |
6335 // and thus sent in multiple frames when using HTTP/2. | |
6336 TEST_F(SpdyNetworkTransactionTest, LargeRequest) { | |
6337 const SpdyString kKey("foo"); | |
6338 const SpdyString kValue(1 << 15, 'z'); | |
6339 | |
6340 HttpRequestInfo request; | |
6341 request.method = "GET"; | |
6342 request.url = default_url_; | |
6343 request.extra_headers.SetHeader(kKey, kValue); | |
6344 | |
6345 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
6346 headers[kKey] = kValue; | |
6347 SpdySerializedFrame req( | |
6348 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
6349 MockWrite writes[] = { | |
6350 CreateMockWrite(req, 0), | |
6351 }; | |
6352 | |
6353 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
6354 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
6355 MockRead reads[] = { | |
6356 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
6357 MockRead(ASYNC, 0, 3) // EOF | |
6358 }; | |
6359 | |
6360 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
6361 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
6362 NetLogWithSource(), nullptr); | |
6363 helper.RunToCompletion(&data); | |
6364 TransactionHelperResult out = helper.output(); | |
6365 | |
6366 EXPECT_THAT(out.rv, IsOk()); | |
6367 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
6368 EXPECT_EQ("hello!", out.response_data); | |
6369 } | |
6370 | |
6371 // Regression test for https://crbug.com/535629: response header exceeds 16 kB. | |
6372 TEST_F(SpdyNetworkTransactionTest, LargeResponseHeader) { | |
6373 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
6374 SpdySerializedFrame req( | |
6375 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
6376 MockWrite writes[] = { | |
6377 CreateMockWrite(req, 0), | |
6378 }; | |
6379 | |
6380 // HPACK decoder implementation limits string literal length to 16 kB. | |
6381 const char* response_headers[2]; | |
6382 const SpdyString kKey(16 * 1024, 'a'); | |
6383 response_headers[0] = kKey.data(); | |
6384 const SpdyString kValue(16 * 1024, 'b'); | |
6385 response_headers[1] = kValue.data(); | |
6386 | |
6387 SpdySerializedFrame resp( | |
6388 spdy_util_.ConstructSpdyGetReply(response_headers, 1, 1)); | |
6389 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
6390 MockRead reads[] = { | |
6391 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
6392 MockRead(ASYNC, 0, 3) // EOF | |
6393 }; | |
6394 | |
6395 HttpRequestInfo request; | |
6396 request.method = "GET"; | |
6397 request.url = default_url_; | |
6398 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
6399 NetLogWithSource(), nullptr); | |
6400 | |
6401 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
6402 helper.RunToCompletion(&data); | |
6403 TransactionHelperResult out = helper.output(); | |
6404 | |
6405 EXPECT_THAT(out.rv, IsOk()); | |
6406 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
6407 EXPECT_EQ("hello!", out.response_data); | |
6408 ASSERT_TRUE(out.response_info.headers->HasHeaderValue(kKey, kValue)); | |
6409 } | |
6410 | |
6411 // End of line delimiter is forbidden according to RFC 7230 Section 3.2. | |
6412 TEST_F(SpdyNetworkTransactionTest, CRLFInHeaderValue) { | |
6413 SpdySerializedFrame req( | |
6414 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
6415 SpdySerializedFrame rst( | |
6416 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
6417 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(rst, 2)}; | |
6418 | |
6419 const char* response_headers[] = {"folded", "foo\r\nbar"}; | |
6420 SpdySerializedFrame resp( | |
6421 spdy_util_.ConstructSpdyGetReply(response_headers, 1, 1)); | |
6422 MockRead reads[] = {CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3)}; | |
6423 | |
6424 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
6425 | |
6426 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
6427 NetLogWithSource(), nullptr); | |
6428 helper.RunToCompletion(&data); | |
6429 TransactionHelperResult out = helper.output(); | |
6430 | |
6431 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
6432 } | |
6433 | |
6434 // Regression test for https://crbug.com/603182. | |
6435 // No response headers received before RST_STREAM: error. | |
6436 TEST_F(SpdyNetworkTransactionTest, RstStreamNoError) { | |
6437 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
6438 MockWrite writes[] = {CreateMockWrite(req, 0, ASYNC)}; | |
6439 | |
6440 SpdySerializedFrame rst( | |
6441 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_NO_ERROR)); | |
6442 MockRead reads[] = {CreateMockRead(rst, 1), MockRead(ASYNC, 0, 2)}; | |
6443 | |
6444 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
6445 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
6446 DEFAULT_PRIORITY, NetLogWithSource(), | |
6447 nullptr); | |
6448 helper.RunToCompletion(&data); | |
6449 TransactionHelperResult out = helper.output(); | |
6450 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
6451 } | |
6452 | |
6453 // Regression test for https://crbug.com/603182. | |
6454 // Response headers and data, then RST_STREAM received, | |
6455 // before request body is sent: success. | |
6456 TEST_F(SpdyNetworkTransactionTest, RstStreamNoErrorAfterResponse) { | |
6457 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
6458 MockWrite writes[] = {CreateMockWrite(req, 0, ASYNC)}; | |
6459 | |
6460 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
6461 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
6462 SpdySerializedFrame rst( | |
6463 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_NO_ERROR)); | |
6464 MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
6465 CreateMockRead(rst, 3), MockRead(ASYNC, 0, 4)}; | |
6466 | |
6467 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
6468 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
6469 DEFAULT_PRIORITY, NetLogWithSource(), | |
6470 nullptr); | |
6471 helper.RunToCompletion(&data); | |
6472 TransactionHelperResult out = helper.output(); | |
6473 EXPECT_THAT(out.rv, IsOk()); | |
6474 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
6475 EXPECT_EQ("hello!", out.response_data); | |
6476 } | |
6477 | |
6478 TEST_F(SpdyNetworkTransactionTest, 100Continue) { | |
6479 SpdySerializedFrame req( | |
6480 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
6481 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
6482 | |
6483 SpdyHeaderBlock informational_headers; | |
6484 informational_headers[spdy_util_.GetStatusKey()] = "100"; | |
6485 SpdySerializedFrame informational_response( | |
6486 spdy_util_.ConstructSpdyReply(1, std::move(informational_headers))); | |
6487 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
6488 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
6489 MockRead reads[] = { | |
6490 CreateMockRead(informational_response, 1), CreateMockRead(resp, 2), | |
6491 CreateMockRead(body, 3), MockRead(ASYNC, 0, 4) // EOF | |
6492 }; | |
6493 | |
6494 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
6495 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
6496 NetLogWithSource(), nullptr); | |
6497 helper.RunToCompletion(&data); | |
6498 TransactionHelperResult out = helper.output(); | |
6499 EXPECT_THAT(out.rv, IsOk()); | |
6500 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
6501 EXPECT_EQ("hello!", out.response_data); | |
6502 } | |
6503 | |
6504 // "A server can send a complete response prior to the client sending an entire | |
6505 // request if the response does not depend on any portion of the request that | |
6506 // has not been sent and received." (RFC7540 Section 8.1) | |
6507 // Regression test for https://crbug.com/606990. Server responds before POST | |
6508 // data are sent and closes connection: this must result in | |
6509 // ERR_CONNECTION_CLOSED (as opposed to ERR_SPDY_PROTOCOL_ERROR). | |
6510 TEST_F(SpdyNetworkTransactionTest, ResponseBeforePostDataSent) { | |
6511 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
6512 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
6513 | |
6514 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
6515 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
6516 MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
6517 MockRead(ASYNC, 0, 3)}; | |
6518 | |
6519 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
6520 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
6521 DEFAULT_PRIORITY, NetLogWithSource(), | |
6522 nullptr); | |
6523 | |
6524 helper.RunPreTestSetup(); | |
6525 helper.AddData(&data); | |
6526 helper.StartDefaultTest(); | |
6527 EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING)); | |
6528 helper.WaitForCallbackToComplete(); | |
6529 EXPECT_THAT(helper.output().rv, IsError(ERR_CONNECTION_CLOSED)); | |
6530 } | |
6531 | |
6532 // Regression test for https://crbug.com/606990. | |
6533 // Server responds before POST data are sent and resets stream with NO_ERROR. | |
6534 TEST_F(SpdyNetworkTransactionTest, ResponseAndRstStreamBeforePostDataSent) { | |
6535 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
6536 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
6537 | |
6538 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
6539 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
6540 SpdySerializedFrame rst( | |
6541 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_NO_ERROR)); | |
6542 MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
6543 CreateMockRead(rst, 3), MockRead(ASYNC, 0, 4)}; | |
6544 | |
6545 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
6546 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
6547 DEFAULT_PRIORITY, NetLogWithSource(), | |
6548 nullptr); | |
6549 | |
6550 helper.RunToCompletion(&data); | |
6551 | |
6552 TransactionHelperResult out = helper.output(); | |
6553 EXPECT_THAT(out.rv, IsOk()); | |
6554 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
6555 EXPECT_EQ("hello!", out.response_data); | |
6556 } | |
6557 | |
6558 // Unsupported frames must be ignored. This is especially important for frame | |
6559 // type 0xb, which used to be the BLOCKED frame in previous versions of SPDY, | |
6560 // but is going to be used for the ORIGIN frame. | |
6561 // TODO(bnc): Implement ORIGIN frame support. https://crbug.com/697333 | |
6562 TEST_F(SpdyNetworkTransactionTest, IgnoreUnsupportedOriginFrame) { | |
6563 SpdySerializedFrame req( | |
6564 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
6565 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
6566 | |
6567 const char origin_frame_on_stream_zero[] = { | |
6568 0x00, 0x00, 0x05, // Length | |
6569 0x0b, // Type | |
6570 0x00, // Flags | |
6571 0x00, 0x00, 0x00, 0x00, // Stream ID | |
6572 0x00, 0x03, // Origin-Len | |
6573 'f', 'o', 'o' // ASCII-Origin | |
6574 }; | |
6575 | |
6576 const char origin_frame_on_stream_one[] = { | |
6577 0x00, 0x00, 0x05, // Length | |
6578 0x0b, // Type | |
6579 0x00, // Flags | |
6580 0x00, 0x00, 0x00, 0x01, // Stream ID | |
6581 0x00, 0x03, // Origin-Len | |
6582 'b', 'a', 'r' // ASCII-Origin | |
6583 }; | |
6584 | |
6585 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
6586 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
6587 MockRead reads[] = {MockRead(ASYNC, origin_frame_on_stream_zero, | |
6588 arraysize(origin_frame_on_stream_zero), 1), | |
6589 CreateMockRead(resp, 2), | |
6590 MockRead(ASYNC, origin_frame_on_stream_one, | |
6591 arraysize(origin_frame_on_stream_one), 3), | |
6592 CreateMockRead(body, 4), MockRead(ASYNC, 0, 5)}; | |
6593 | |
6594 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
6595 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
6596 NetLogWithSource(), nullptr); | |
6597 helper.RunToCompletion(&data); | |
6598 TransactionHelperResult out = helper.output(); | |
6599 EXPECT_THAT(out.rv, IsOk()); | |
6600 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
6601 EXPECT_EQ("hello!", out.response_data); | |
6602 } | |
6603 | |
6604 class SpdyNetworkTransactionTLSUsageCheckTest | |
6605 : public SpdyNetworkTransactionTest { | |
6606 protected: | |
6607 void RunTLSUsageCheckTest( | |
6608 std::unique_ptr<SSLSocketDataProvider> ssl_provider) { | |
6609 SpdySerializedFrame goaway( | |
6610 spdy_util_.ConstructSpdyGoAway(0, ERROR_CODE_INADEQUATE_SECURITY, "")); | |
6611 MockWrite writes[] = {CreateMockWrite(goaway)}; | |
6612 | |
6613 StaticSocketDataProvider data(nullptr, 0, writes, arraysize(writes)); | |
6614 HttpRequestInfo request; | |
6615 request.method = "GET"; | |
6616 request.url = default_url_; | |
6617 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
6618 NetLogWithSource(), nullptr); | |
6619 helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider)); | |
6620 TransactionHelperResult out = helper.output(); | |
6621 EXPECT_THAT(out.rv, IsError(ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY)); | |
6622 } | |
6623 }; | |
6624 | |
6625 TEST_F(SpdyNetworkTransactionTLSUsageCheckTest, TLSVersionTooOld) { | |
6626 std::unique_ptr<SSLSocketDataProvider> ssl_provider( | |
6627 new SSLSocketDataProvider(ASYNC, OK)); | |
6628 SSLConnectionStatusSetVersion(SSL_CONNECTION_VERSION_SSL3, | |
6629 &ssl_provider->connection_status); | |
6630 | |
6631 RunTLSUsageCheckTest(std::move(ssl_provider)); | |
6632 } | |
6633 | |
6634 TEST_F(SpdyNetworkTransactionTLSUsageCheckTest, TLSCipherSuiteSucky) { | |
6635 std::unique_ptr<SSLSocketDataProvider> ssl_provider( | |
6636 new SSLSocketDataProvider(ASYNC, OK)); | |
6637 // Set to TLS_RSA_WITH_NULL_MD5 | |
6638 SSLConnectionStatusSetCipherSuite(0x1, &ssl_provider->connection_status); | |
6639 | |
6640 RunTLSUsageCheckTest(std::move(ssl_provider)); | |
6641 } | |
6642 | |
6643 } // namespace net | |
OLD | NEW |