| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/spdy/spdy_http_stream.h" | |
| 6 | |
| 7 #include <stdint.h> | |
| 8 | |
| 9 #include <memory> | |
| 10 | |
| 11 #include "base/run_loop.h" | |
| 12 #include "base/stl_util.h" | |
| 13 #include "base/threading/thread_task_runner_handle.h" | |
| 14 #include "crypto/ec_private_key.h" | |
| 15 #include "crypto/ec_signature_creator.h" | |
| 16 #include "crypto/signature_creator.h" | |
| 17 #include "net/base/chunked_upload_data_stream.h" | |
| 18 #include "net/base/load_timing_info.h" | |
| 19 #include "net/base/load_timing_info_test_util.h" | |
| 20 #include "net/base/test_completion_callback.h" | |
| 21 #include "net/cert/asn1_util.h" | |
| 22 #include "net/http/http_request_info.h" | |
| 23 #include "net/http/http_response_headers.h" | |
| 24 #include "net/http/http_response_info.h" | |
| 25 #include "net/log/net_log_with_source.h" | |
| 26 #include "net/log/test_net_log.h" | |
| 27 #include "net/socket/socket_test_util.h" | |
| 28 #include "net/spdy/platform/api/spdy_string.h" | |
| 29 #include "net/spdy/spdy_http_utils.h" | |
| 30 #include "net/spdy/spdy_session.h" | |
| 31 #include "net/spdy/spdy_test_util_common.h" | |
| 32 #include "net/ssl/default_channel_id_store.h" | |
| 33 #include "net/test/cert_test_util.h" | |
| 34 #include "net/test/gtest_util.h" | |
| 35 #include "net/test/test_data_directory.h" | |
| 36 #include "testing/gmock/include/gmock/gmock.h" | |
| 37 #include "testing/gtest/include/gtest/gtest.h" | |
| 38 | |
| 39 namespace net { | |
| 40 | |
| 41 namespace test { | |
| 42 | |
| 43 namespace { | |
| 44 | |
| 45 // Tests the load timing of a stream that's connected and is not the first | |
| 46 // request sent on a connection. | |
| 47 void TestLoadTimingReused(const HttpStream& stream) { | |
| 48 LoadTimingInfo load_timing_info; | |
| 49 EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info)); | |
| 50 | |
| 51 EXPECT_TRUE(load_timing_info.socket_reused); | |
| 52 EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id); | |
| 53 | |
| 54 ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); | |
| 55 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info); | |
| 56 } | |
| 57 | |
| 58 // Tests the load timing of a stream that's connected and using a fresh | |
| 59 // connection. | |
| 60 void TestLoadTimingNotReused(const HttpStream& stream) { | |
| 61 LoadTimingInfo load_timing_info; | |
| 62 EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info)); | |
| 63 | |
| 64 EXPECT_FALSE(load_timing_info.socket_reused); | |
| 65 EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id); | |
| 66 | |
| 67 ExpectConnectTimingHasTimes( | |
| 68 load_timing_info.connect_timing, | |
| 69 CONNECT_TIMING_HAS_DNS_TIMES | CONNECT_TIMING_HAS_SSL_TIMES); | |
| 70 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info); | |
| 71 } | |
| 72 | |
| 73 class ReadErrorUploadDataStream : public UploadDataStream { | |
| 74 public: | |
| 75 enum class FailureMode { SYNC, ASYNC }; | |
| 76 | |
| 77 explicit ReadErrorUploadDataStream(FailureMode mode) | |
| 78 : UploadDataStream(true, 0), async_(mode), weak_factory_(this) {} | |
| 79 | |
| 80 private: | |
| 81 void CompleteRead() { UploadDataStream::OnReadCompleted(ERR_FAILED); } | |
| 82 | |
| 83 // UploadDataStream implementation: | |
| 84 int InitInternal(const NetLogWithSource& net_log) override { return OK; } | |
| 85 | |
| 86 int ReadInternal(IOBuffer* buf, int buf_len) override { | |
| 87 if (async_ == FailureMode::ASYNC) { | |
| 88 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 89 FROM_HERE, base::Bind(&ReadErrorUploadDataStream::CompleteRead, | |
| 90 weak_factory_.GetWeakPtr())); | |
| 91 return ERR_IO_PENDING; | |
| 92 } | |
| 93 return ERR_FAILED; | |
| 94 } | |
| 95 | |
| 96 void ResetInternal() override {} | |
| 97 | |
| 98 const FailureMode async_; | |
| 99 | |
| 100 base::WeakPtrFactory<ReadErrorUploadDataStream> weak_factory_; | |
| 101 | |
| 102 DISALLOW_COPY_AND_ASSIGN(ReadErrorUploadDataStream); | |
| 103 }; | |
| 104 | |
| 105 class CancelStreamCallback : public TestCompletionCallbackBase { | |
| 106 public: | |
| 107 explicit CancelStreamCallback(SpdyHttpStream* stream) | |
| 108 : stream_(stream), | |
| 109 callback_(base::Bind(&CancelStreamCallback::CancelStream, | |
| 110 base::Unretained(this))) {} | |
| 111 | |
| 112 const CompletionCallback& callback() const { return callback_; } | |
| 113 | |
| 114 private: | |
| 115 void CancelStream(int result) { | |
| 116 stream_->Cancel(); | |
| 117 SetResult(result); | |
| 118 } | |
| 119 | |
| 120 SpdyHttpStream* stream_; | |
| 121 CompletionCallback callback_; | |
| 122 }; | |
| 123 | |
| 124 } // namespace | |
| 125 | |
| 126 class SpdyHttpStreamTest : public testing::Test { | |
| 127 public: | |
| 128 SpdyHttpStreamTest() | |
| 129 : url_(kDefaultUrl), | |
| 130 host_port_pair_(HostPortPair::FromURL(url_)), | |
| 131 key_(host_port_pair_, ProxyServer::Direct(), PRIVACY_MODE_DISABLED), | |
| 132 ssl_(SYNCHRONOUS, OK) { | |
| 133 session_deps_.net_log = &net_log_; | |
| 134 } | |
| 135 | |
| 136 ~SpdyHttpStreamTest() override {} | |
| 137 | |
| 138 protected: | |
| 139 void TearDown() override { | |
| 140 crypto::ECSignatureCreator::SetFactoryForTesting(nullptr); | |
| 141 base::RunLoop().RunUntilIdle(); | |
| 142 EXPECT_TRUE(sequenced_data_->AllReadDataConsumed()); | |
| 143 EXPECT_TRUE(sequenced_data_->AllWriteDataConsumed()); | |
| 144 } | |
| 145 | |
| 146 // Initializes the session using SequencedSocketData. | |
| 147 void InitSession(MockRead* reads, | |
| 148 size_t reads_count, | |
| 149 MockWrite* writes, | |
| 150 size_t writes_count) { | |
| 151 sequenced_data_.reset( | |
| 152 new SequencedSocketData(reads, reads_count, writes, writes_count)); | |
| 153 session_deps_.socket_factory->AddSocketDataProvider(sequenced_data_.get()); | |
| 154 | |
| 155 ssl_.cert = ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); | |
| 156 ASSERT_TRUE(ssl_.cert); | |
| 157 session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_); | |
| 158 | |
| 159 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); | |
| 160 session_ = | |
| 161 CreateSecureSpdySession(http_session_.get(), key_, NetLogWithSource()); | |
| 162 } | |
| 163 | |
| 164 void TestSendCredentials(ChannelIDService* channel_id_service, | |
| 165 const SpdyString& cert, | |
| 166 const SpdyString& proof); | |
| 167 | |
| 168 SpdyTestUtil spdy_util_; | |
| 169 TestNetLog net_log_; | |
| 170 SpdySessionDependencies session_deps_; | |
| 171 const GURL url_; | |
| 172 const HostPortPair host_port_pair_; | |
| 173 const SpdySessionKey key_; | |
| 174 std::unique_ptr<SequencedSocketData> sequenced_data_; | |
| 175 std::unique_ptr<HttpNetworkSession> http_session_; | |
| 176 base::WeakPtr<SpdySession> session_; | |
| 177 | |
| 178 private: | |
| 179 MockECSignatureCreatorFactory ec_signature_creator_factory_; | |
| 180 SSLSocketDataProvider ssl_; | |
| 181 }; | |
| 182 | |
| 183 TEST_F(SpdyHttpStreamTest, SendRequest) { | |
| 184 SpdySerializedFrame req( | |
| 185 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 186 MockWrite writes[] = { | |
| 187 CreateMockWrite(req, 0), | |
| 188 }; | |
| 189 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 190 MockRead reads[] = { | |
| 191 CreateMockRead(resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF | |
| 192 }; | |
| 193 | |
| 194 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 195 | |
| 196 HttpRequestInfo request; | |
| 197 request.method = "GET"; | |
| 198 request.url = url_; | |
| 199 TestCompletionCallback callback; | |
| 200 HttpResponseInfo response; | |
| 201 HttpRequestHeaders headers; | |
| 202 NetLogWithSource net_log; | |
| 203 std::unique_ptr<SpdyHttpStream> http_stream( | |
| 204 new SpdyHttpStream(session_, true, net_log.source())); | |
| 205 // Make sure getting load timing information the stream early does not crash. | |
| 206 LoadTimingInfo load_timing_info; | |
| 207 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); | |
| 208 | |
| 209 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 210 CompletionCallback()), | |
| 211 IsOk()); | |
| 212 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); | |
| 213 | |
| 214 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
| 215 IsError(ERR_IO_PENDING)); | |
| 216 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 217 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); | |
| 218 | |
| 219 callback.WaitForResult(); | |
| 220 | |
| 221 // Can get timing information once the stream connects. | |
| 222 TestLoadTimingNotReused(*http_stream); | |
| 223 | |
| 224 // Because we abandoned the stream, we don't expect to find a session in the | |
| 225 // pool anymore. | |
| 226 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 227 | |
| 228 TestLoadTimingNotReused(*http_stream); | |
| 229 http_stream->Close(true); | |
| 230 // Test that there's no crash when trying to get the load timing after the | |
| 231 // stream has been closed. | |
| 232 TestLoadTimingNotReused(*http_stream); | |
| 233 | |
| 234 EXPECT_EQ(static_cast<int64_t>(req.size()), http_stream->GetTotalSentBytes()); | |
| 235 EXPECT_EQ(static_cast<int64_t>(resp.size()), | |
| 236 http_stream->GetTotalReceivedBytes()); | |
| 237 } | |
| 238 | |
| 239 TEST_F(SpdyHttpStreamTest, LoadTimingTwoRequests) { | |
| 240 SpdySerializedFrame req1( | |
| 241 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 242 SpdySerializedFrame req2( | |
| 243 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
| 244 MockWrite writes[] = { | |
| 245 CreateMockWrite(req1, 0), CreateMockWrite(req2, 1), | |
| 246 }; | |
| 247 SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 248 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, "", 0, true)); | |
| 249 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 250 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, "", 0, true)); | |
| 251 MockRead reads[] = { | |
| 252 CreateMockRead(resp1, 2), CreateMockRead(body1, 3), | |
| 253 CreateMockRead(resp2, 4), CreateMockRead(body2, 5), | |
| 254 MockRead(ASYNC, 0, 6) // EOF | |
| 255 }; | |
| 256 | |
| 257 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 258 | |
| 259 HttpRequestInfo request1; | |
| 260 request1.method = "GET"; | |
| 261 request1.url = url_; | |
| 262 TestCompletionCallback callback1; | |
| 263 HttpResponseInfo response1; | |
| 264 HttpRequestHeaders headers1; | |
| 265 NetLogWithSource net_log; | |
| 266 std::unique_ptr<SpdyHttpStream> http_stream1( | |
| 267 new SpdyHttpStream(session_, true, net_log.source())); | |
| 268 | |
| 269 HttpRequestInfo request2; | |
| 270 request2.method = "GET"; | |
| 271 request2.url = url_; | |
| 272 TestCompletionCallback callback2; | |
| 273 HttpResponseInfo response2; | |
| 274 HttpRequestHeaders headers2; | |
| 275 std::unique_ptr<SpdyHttpStream> http_stream2( | |
| 276 new SpdyHttpStream(session_, true, net_log.source())); | |
| 277 | |
| 278 // First write. | |
| 279 ASSERT_THAT(http_stream1->InitializeStream(&request1, DEFAULT_PRIORITY, | |
| 280 net_log, CompletionCallback()), | |
| 281 IsOk()); | |
| 282 EXPECT_THAT( | |
| 283 http_stream1->SendRequest(headers1, &response1, callback1.callback()), | |
| 284 IsError(ERR_IO_PENDING)); | |
| 285 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 286 | |
| 287 EXPECT_LE(0, callback1.WaitForResult()); | |
| 288 | |
| 289 TestLoadTimingNotReused(*http_stream1); | |
| 290 LoadTimingInfo load_timing_info1; | |
| 291 LoadTimingInfo load_timing_info2; | |
| 292 EXPECT_TRUE(http_stream1->GetLoadTimingInfo(&load_timing_info1)); | |
| 293 EXPECT_FALSE(http_stream2->GetLoadTimingInfo(&load_timing_info2)); | |
| 294 | |
| 295 // Second write. | |
| 296 ASSERT_THAT(http_stream2->InitializeStream(&request2, DEFAULT_PRIORITY, | |
| 297 net_log, CompletionCallback()), | |
| 298 IsOk()); | |
| 299 EXPECT_THAT( | |
| 300 http_stream2->SendRequest(headers2, &response2, callback2.callback()), | |
| 301 IsError(ERR_IO_PENDING)); | |
| 302 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 303 | |
| 304 EXPECT_LE(0, callback2.WaitForResult()); | |
| 305 | |
| 306 // Perform all async reads. | |
| 307 base::RunLoop().RunUntilIdle(); | |
| 308 | |
| 309 TestLoadTimingReused(*http_stream2); | |
| 310 EXPECT_TRUE(http_stream2->GetLoadTimingInfo(&load_timing_info2)); | |
| 311 EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id); | |
| 312 | |
| 313 // Read stream 1 to completion, before making sure we can still read load | |
| 314 // timing from both streams. | |
| 315 scoped_refptr<IOBuffer> buf1(new IOBuffer(1)); | |
| 316 ASSERT_EQ( | |
| 317 0, http_stream1->ReadResponseBody(buf1.get(), 1, callback1.callback())); | |
| 318 | |
| 319 // Stream 1 has been read to completion. | |
| 320 TestLoadTimingNotReused(*http_stream1); | |
| 321 | |
| 322 EXPECT_EQ(static_cast<int64_t>(req1.size()), | |
| 323 http_stream1->GetTotalSentBytes()); | |
| 324 EXPECT_EQ(static_cast<int64_t>(resp1.size() + body1.size()), | |
| 325 http_stream1->GetTotalReceivedBytes()); | |
| 326 | |
| 327 // Stream 2 still has queued body data. | |
| 328 TestLoadTimingReused(*http_stream2); | |
| 329 | |
| 330 EXPECT_EQ(static_cast<int64_t>(req2.size()), | |
| 331 http_stream2->GetTotalSentBytes()); | |
| 332 EXPECT_EQ(static_cast<int64_t>(resp2.size() + body2.size()), | |
| 333 http_stream2->GetTotalReceivedBytes()); | |
| 334 } | |
| 335 | |
| 336 TEST_F(SpdyHttpStreamTest, SendChunkedPost) { | |
| 337 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 338 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, kUploadData, | |
| 339 kUploadDataSize, | |
| 340 /*fin=*/true)); | |
| 341 MockWrite writes[] = { | |
| 342 CreateMockWrite(req, 0), // request | |
| 343 CreateMockWrite(body, 1) // POST upload frame | |
| 344 }; | |
| 345 | |
| 346 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 347 MockRead reads[] = { | |
| 348 CreateMockRead(resp, 2), CreateMockRead(body, 3, SYNCHRONOUS), | |
| 349 MockRead(SYNCHRONOUS, 0, 4) // EOF | |
| 350 }; | |
| 351 | |
| 352 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 353 | |
| 354 ChunkedUploadDataStream upload_stream(0); | |
| 355 const int kFirstChunkSize = kUploadDataSize/2; | |
| 356 upload_stream.AppendData(kUploadData, kFirstChunkSize, false); | |
| 357 upload_stream.AppendData(kUploadData + kFirstChunkSize, | |
| 358 kUploadDataSize - kFirstChunkSize, true); | |
| 359 | |
| 360 HttpRequestInfo request; | |
| 361 request.method = "POST"; | |
| 362 request.url = url_; | |
| 363 request.upload_data_stream = &upload_stream; | |
| 364 | |
| 365 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
| 366 NetLogWithSource()), | |
| 367 IsOk()); | |
| 368 | |
| 369 TestCompletionCallback callback; | |
| 370 HttpResponseInfo response; | |
| 371 HttpRequestHeaders headers; | |
| 372 NetLogWithSource net_log; | |
| 373 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
| 374 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 375 CompletionCallback()), | |
| 376 IsOk()); | |
| 377 | |
| 378 EXPECT_THAT(http_stream.SendRequest(headers, &response, callback.callback()), | |
| 379 IsError(ERR_IO_PENDING)); | |
| 380 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 381 | |
| 382 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
| 383 | |
| 384 EXPECT_EQ(static_cast<int64_t>(req.size() + body.size()), | |
| 385 http_stream.GetTotalSentBytes()); | |
| 386 EXPECT_EQ(static_cast<int64_t>(resp.size() + body.size()), | |
| 387 http_stream.GetTotalReceivedBytes()); | |
| 388 | |
| 389 // Because the server closed the connection, we there shouldn't be a session | |
| 390 // in the pool anymore. | |
| 391 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 392 } | |
| 393 | |
| 394 // This unittest tests the request callback is properly called and handled. | |
| 395 TEST_F(SpdyHttpStreamTest, SendChunkedPostLastEmpty) { | |
| 396 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 397 SpdySerializedFrame chunk( | |
| 398 spdy_util_.ConstructSpdyDataFrame(1, nullptr, 0, true)); | |
| 399 MockWrite writes[] = { | |
| 400 CreateMockWrite(req, 0), // request | |
| 401 CreateMockWrite(chunk, 1), | |
| 402 }; | |
| 403 | |
| 404 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 405 MockRead reads[] = { | |
| 406 CreateMockRead(resp, 2), CreateMockRead(chunk, 3, SYNCHRONOUS), | |
| 407 MockRead(SYNCHRONOUS, 0, 4) // EOF | |
| 408 }; | |
| 409 | |
| 410 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 411 | |
| 412 ChunkedUploadDataStream upload_stream(0); | |
| 413 upload_stream.AppendData(nullptr, 0, true); | |
| 414 | |
| 415 HttpRequestInfo request; | |
| 416 request.method = "POST"; | |
| 417 request.url = url_; | |
| 418 request.upload_data_stream = &upload_stream; | |
| 419 | |
| 420 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
| 421 NetLogWithSource()), | |
| 422 IsOk()); | |
| 423 | |
| 424 TestCompletionCallback callback; | |
| 425 HttpResponseInfo response; | |
| 426 HttpRequestHeaders headers; | |
| 427 NetLogWithSource net_log; | |
| 428 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
| 429 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 430 CompletionCallback()), | |
| 431 IsOk()); | |
| 432 EXPECT_THAT(http_stream.SendRequest(headers, &response, callback.callback()), | |
| 433 IsError(ERR_IO_PENDING)); | |
| 434 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 435 | |
| 436 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
| 437 | |
| 438 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk.size()), | |
| 439 http_stream.GetTotalSentBytes()); | |
| 440 EXPECT_EQ(static_cast<int64_t>(resp.size() + chunk.size()), | |
| 441 http_stream.GetTotalReceivedBytes()); | |
| 442 | |
| 443 // Because the server closed the connection, there shouldn't be a session | |
| 444 // in the pool anymore. | |
| 445 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 446 } | |
| 447 | |
| 448 TEST_F(SpdyHttpStreamTest, ConnectionClosedDuringChunkedPost) { | |
| 449 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 450 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, kUploadData, | |
| 451 kUploadDataSize, | |
| 452 /*fin=*/false)); | |
| 453 MockWrite writes[] = { | |
| 454 CreateMockWrite(req, 0), // Request | |
| 455 CreateMockWrite(body, 1) // First POST upload frame | |
| 456 }; | |
| 457 | |
| 458 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 459 MockRead reads[] = { | |
| 460 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 2) // Server hangs up early. | |
| 461 }; | |
| 462 | |
| 463 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 464 | |
| 465 ChunkedUploadDataStream upload_stream(0); | |
| 466 // Append first chunk. | |
| 467 upload_stream.AppendData(kUploadData, kUploadDataSize, false); | |
| 468 | |
| 469 HttpRequestInfo request; | |
| 470 request.method = "POST"; | |
| 471 request.url = url_; | |
| 472 request.upload_data_stream = &upload_stream; | |
| 473 | |
| 474 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
| 475 NetLogWithSource()), | |
| 476 IsOk()); | |
| 477 | |
| 478 TestCompletionCallback callback; | |
| 479 HttpResponseInfo response; | |
| 480 HttpRequestHeaders headers; | |
| 481 NetLogWithSource net_log; | |
| 482 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
| 483 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 484 CompletionCallback()), | |
| 485 IsOk()); | |
| 486 | |
| 487 EXPECT_THAT(http_stream.SendRequest(headers, &response, callback.callback()), | |
| 488 IsError(ERR_IO_PENDING)); | |
| 489 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 490 | |
| 491 EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CONNECTION_CLOSED)); | |
| 492 | |
| 493 EXPECT_EQ(static_cast<int64_t>(req.size() + body.size()), | |
| 494 http_stream.GetTotalSentBytes()); | |
| 495 EXPECT_EQ(0, http_stream.GetTotalReceivedBytes()); | |
| 496 | |
| 497 // Because the server closed the connection, we there shouldn't be a session | |
| 498 // in the pool anymore. | |
| 499 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 500 | |
| 501 // Appending a second chunk now should not result in a crash. | |
| 502 upload_stream.AppendData(kUploadData, kUploadDataSize, true); | |
| 503 // Appending data is currently done synchronously, but seems best to be | |
| 504 // paranoid. | |
| 505 base::RunLoop().RunUntilIdle(); | |
| 506 | |
| 507 // The total sent and received bytes should be unchanged. | |
| 508 EXPECT_EQ(static_cast<int64_t>(req.size() + body.size()), | |
| 509 http_stream.GetTotalSentBytes()); | |
| 510 EXPECT_EQ(0, http_stream.GetTotalReceivedBytes()); | |
| 511 } | |
| 512 | |
| 513 // Test to ensure the SpdyStream state machine does not get confused when a | |
| 514 // chunk becomes available while a write is pending. | |
| 515 TEST_F(SpdyHttpStreamTest, DelayedSendChunkedPost) { | |
| 516 const char kUploadData1[] = "12345678"; | |
| 517 const int kUploadData1Size = arraysize(kUploadData1)-1; | |
| 518 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 519 SpdySerializedFrame chunk1(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 520 SpdySerializedFrame chunk2(spdy_util_.ConstructSpdyDataFrame( | |
| 521 1, kUploadData1, kUploadData1Size, false)); | |
| 522 SpdySerializedFrame chunk3(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 523 MockWrite writes[] = { | |
| 524 CreateMockWrite(req, 0), | |
| 525 CreateMockWrite(chunk1, 1), // POST upload frames | |
| 526 CreateMockWrite(chunk2, 2), CreateMockWrite(chunk3, 3), | |
| 527 }; | |
| 528 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 529 MockRead reads[] = { | |
| 530 CreateMockRead(resp, 4), CreateMockRead(chunk1, 5), | |
| 531 CreateMockRead(chunk2, 6), CreateMockRead(chunk3, 7), | |
| 532 MockRead(ASYNC, 0, 8) // EOF | |
| 533 }; | |
| 534 | |
| 535 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 536 | |
| 537 ChunkedUploadDataStream upload_stream(0); | |
| 538 | |
| 539 HttpRequestInfo request; | |
| 540 request.method = "POST"; | |
| 541 request.url = url_; | |
| 542 request.upload_data_stream = &upload_stream; | |
| 543 | |
| 544 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
| 545 NetLogWithSource()), | |
| 546 IsOk()); | |
| 547 upload_stream.AppendData(kUploadData, kUploadDataSize, false); | |
| 548 | |
| 549 NetLogWithSource net_log; | |
| 550 std::unique_ptr<SpdyHttpStream> http_stream( | |
| 551 new SpdyHttpStream(session_, true, net_log.source())); | |
| 552 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 553 CompletionCallback()), | |
| 554 IsOk()); | |
| 555 | |
| 556 TestCompletionCallback callback; | |
| 557 HttpRequestHeaders headers; | |
| 558 HttpResponseInfo response; | |
| 559 // This will attempt to Write() the initial request and headers, which will | |
| 560 // complete asynchronously. | |
| 561 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
| 562 IsError(ERR_IO_PENDING)); | |
| 563 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 564 | |
| 565 // Complete the initial request write and the first chunk. | |
| 566 base::RunLoop().RunUntilIdle(); | |
| 567 ASSERT_FALSE(callback.have_result()); | |
| 568 | |
| 569 // Now append the final two chunks which will enqueue two more writes. | |
| 570 upload_stream.AppendData(kUploadData1, kUploadData1Size, false); | |
| 571 upload_stream.AppendData(kUploadData, kUploadDataSize, true); | |
| 572 | |
| 573 // Finish writing all the chunks and do all reads. | |
| 574 base::RunLoop().RunUntilIdle(); | |
| 575 ASSERT_TRUE(callback.have_result()); | |
| 576 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
| 577 | |
| 578 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk1.size() + chunk2.size() + | |
| 579 chunk3.size()), | |
| 580 http_stream->GetTotalSentBytes()); | |
| 581 EXPECT_EQ(static_cast<int64_t>(resp.size() + chunk1.size() + chunk2.size() + | |
| 582 chunk3.size()), | |
| 583 http_stream->GetTotalReceivedBytes()); | |
| 584 | |
| 585 // Check response headers. | |
| 586 ASSERT_THAT(http_stream->ReadResponseHeaders(callback.callback()), IsOk()); | |
| 587 | |
| 588 // Check |chunk1| response. | |
| 589 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); | |
| 590 ASSERT_EQ(kUploadDataSize, | |
| 591 http_stream->ReadResponseBody( | |
| 592 buf1.get(), kUploadDataSize, callback.callback())); | |
| 593 EXPECT_EQ(kUploadData, SpdyString(buf1->data(), kUploadDataSize)); | |
| 594 | |
| 595 // Check |chunk2| response. | |
| 596 scoped_refptr<IOBuffer> buf2(new IOBuffer(kUploadData1Size)); | |
| 597 ASSERT_EQ(kUploadData1Size, | |
| 598 http_stream->ReadResponseBody( | |
| 599 buf2.get(), kUploadData1Size, callback.callback())); | |
| 600 EXPECT_EQ(kUploadData1, SpdyString(buf2->data(), kUploadData1Size)); | |
| 601 | |
| 602 // Check |chunk3| response. | |
| 603 scoped_refptr<IOBuffer> buf3(new IOBuffer(kUploadDataSize)); | |
| 604 ASSERT_EQ(kUploadDataSize, | |
| 605 http_stream->ReadResponseBody( | |
| 606 buf3.get(), kUploadDataSize, callback.callback())); | |
| 607 EXPECT_EQ(kUploadData, SpdyString(buf3->data(), kUploadDataSize)); | |
| 608 | |
| 609 ASSERT_TRUE(response.headers.get()); | |
| 610 ASSERT_EQ(200, response.headers->response_code()); | |
| 611 } | |
| 612 | |
| 613 // Test that the SpdyStream state machine can handle sending a final empty data | |
| 614 // frame when uploading a chunked data stream. | |
| 615 TEST_F(SpdyHttpStreamTest, DelayedSendChunkedPostWithEmptyFinalDataFrame) { | |
| 616 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 617 SpdySerializedFrame chunk1(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 618 SpdySerializedFrame chunk2(spdy_util_.ConstructSpdyDataFrame(1, "", 0, true)); | |
| 619 MockWrite writes[] = { | |
| 620 CreateMockWrite(req, 0), | |
| 621 CreateMockWrite(chunk1, 1), // POST upload frames | |
| 622 CreateMockWrite(chunk2, 2), | |
| 623 }; | |
| 624 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 625 MockRead reads[] = { | |
| 626 CreateMockRead(resp, 3), CreateMockRead(chunk1, 4), | |
| 627 CreateMockRead(chunk2, 5), MockRead(ASYNC, 0, 6) // EOF | |
| 628 }; | |
| 629 | |
| 630 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 631 | |
| 632 ChunkedUploadDataStream upload_stream(0); | |
| 633 | |
| 634 HttpRequestInfo request; | |
| 635 request.method = "POST"; | |
| 636 request.url = url_; | |
| 637 request.upload_data_stream = &upload_stream; | |
| 638 | |
| 639 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
| 640 NetLogWithSource()), | |
| 641 IsOk()); | |
| 642 upload_stream.AppendData(kUploadData, kUploadDataSize, false); | |
| 643 | |
| 644 NetLogWithSource net_log; | |
| 645 std::unique_ptr<SpdyHttpStream> http_stream( | |
| 646 new SpdyHttpStream(session_, true, net_log.source())); | |
| 647 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 648 CompletionCallback()), | |
| 649 IsOk()); | |
| 650 | |
| 651 TestCompletionCallback callback; | |
| 652 HttpRequestHeaders headers; | |
| 653 HttpResponseInfo response; | |
| 654 // This will attempt to Write() the initial request and headers, which will | |
| 655 // complete asynchronously. | |
| 656 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
| 657 IsError(ERR_IO_PENDING)); | |
| 658 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 659 | |
| 660 // Complete the initial request write and the first chunk. | |
| 661 base::RunLoop().RunUntilIdle(); | |
| 662 ASSERT_FALSE(callback.have_result()); | |
| 663 | |
| 664 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk1.size()), | |
| 665 http_stream->GetTotalSentBytes()); | |
| 666 EXPECT_EQ(0, http_stream->GetTotalReceivedBytes()); | |
| 667 | |
| 668 // Now end the stream with an empty data frame and the FIN set. | |
| 669 upload_stream.AppendData(nullptr, 0, true); | |
| 670 | |
| 671 // Finish writing the final frame, and perform all reads. | |
| 672 base::RunLoop().RunUntilIdle(); | |
| 673 ASSERT_TRUE(callback.have_result()); | |
| 674 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
| 675 | |
| 676 // Check response headers. | |
| 677 ASSERT_THAT(http_stream->ReadResponseHeaders(callback.callback()), IsOk()); | |
| 678 | |
| 679 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk1.size() + chunk2.size()), | |
| 680 http_stream->GetTotalSentBytes()); | |
| 681 EXPECT_EQ(static_cast<int64_t>(resp.size() + chunk1.size() + chunk2.size()), | |
| 682 http_stream->GetTotalReceivedBytes()); | |
| 683 | |
| 684 // Check |chunk1| response. | |
| 685 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); | |
| 686 ASSERT_EQ(kUploadDataSize, | |
| 687 http_stream->ReadResponseBody( | |
| 688 buf1.get(), kUploadDataSize, callback.callback())); | |
| 689 EXPECT_EQ(kUploadData, SpdyString(buf1->data(), kUploadDataSize)); | |
| 690 | |
| 691 // Check |chunk2| response. | |
| 692 ASSERT_EQ(0, | |
| 693 http_stream->ReadResponseBody( | |
| 694 buf1.get(), kUploadDataSize, callback.callback())); | |
| 695 | |
| 696 ASSERT_TRUE(response.headers.get()); | |
| 697 ASSERT_EQ(200, response.headers->response_code()); | |
| 698 } | |
| 699 | |
| 700 // Test that the SpdyStream state machine handles a chunked upload with no | |
| 701 // payload. Unclear if this is a case worth supporting. | |
| 702 TEST_F(SpdyHttpStreamTest, ChunkedPostWithEmptyPayload) { | |
| 703 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 704 SpdySerializedFrame chunk(spdy_util_.ConstructSpdyDataFrame(1, "", 0, true)); | |
| 705 MockWrite writes[] = { | |
| 706 CreateMockWrite(req, 0), CreateMockWrite(chunk, 1), | |
| 707 }; | |
| 708 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 709 MockRead reads[] = { | |
| 710 CreateMockRead(resp, 2), CreateMockRead(chunk, 3), | |
| 711 MockRead(ASYNC, 0, 4) // EOF | |
| 712 }; | |
| 713 | |
| 714 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 715 | |
| 716 ChunkedUploadDataStream upload_stream(0); | |
| 717 | |
| 718 HttpRequestInfo request; | |
| 719 request.method = "POST"; | |
| 720 request.url = url_; | |
| 721 request.upload_data_stream = &upload_stream; | |
| 722 | |
| 723 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
| 724 NetLogWithSource()), | |
| 725 IsOk()); | |
| 726 upload_stream.AppendData("", 0, true); | |
| 727 | |
| 728 NetLogWithSource net_log; | |
| 729 std::unique_ptr<SpdyHttpStream> http_stream( | |
| 730 new SpdyHttpStream(session_, true, net_log.source())); | |
| 731 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 732 CompletionCallback()), | |
| 733 IsOk()); | |
| 734 | |
| 735 TestCompletionCallback callback; | |
| 736 HttpRequestHeaders headers; | |
| 737 HttpResponseInfo response; | |
| 738 // This will attempt to Write() the initial request and headers, which will | |
| 739 // complete asynchronously. | |
| 740 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
| 741 IsError(ERR_IO_PENDING)); | |
| 742 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 743 | |
| 744 // Complete writing request, followed by a FIN. | |
| 745 base::RunLoop().RunUntilIdle(); | |
| 746 ASSERT_TRUE(callback.have_result()); | |
| 747 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
| 748 | |
| 749 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk.size()), | |
| 750 http_stream->GetTotalSentBytes()); | |
| 751 EXPECT_EQ(static_cast<int64_t>(resp.size() + chunk.size()), | |
| 752 http_stream->GetTotalReceivedBytes()); | |
| 753 | |
| 754 // Check response headers. | |
| 755 ASSERT_THAT(http_stream->ReadResponseHeaders(callback.callback()), IsOk()); | |
| 756 | |
| 757 // Check |chunk| response. | |
| 758 scoped_refptr<IOBuffer> buf(new IOBuffer(1)); | |
| 759 ASSERT_EQ(0, | |
| 760 http_stream->ReadResponseBody( | |
| 761 buf.get(), 1, callback.callback())); | |
| 762 | |
| 763 ASSERT_TRUE(response.headers.get()); | |
| 764 ASSERT_EQ(200, response.headers->response_code()); | |
| 765 } | |
| 766 | |
| 767 // Test case for https://crbug.com/50058. | |
| 768 TEST_F(SpdyHttpStreamTest, SpdyURLTest) { | |
| 769 const char* const full_url = "https://www.example.org/foo?query=what#anchor"; | |
| 770 const char* const base_url = "https://www.example.org/foo?query=what"; | |
| 771 SpdySerializedFrame req(spdy_util_.ConstructSpdyGet(base_url, 1, LOWEST)); | |
| 772 MockWrite writes[] = { | |
| 773 CreateMockWrite(req, 0), | |
| 774 }; | |
| 775 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 776 MockRead reads[] = { | |
| 777 CreateMockRead(resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF | |
| 778 }; | |
| 779 | |
| 780 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 781 | |
| 782 HttpRequestInfo request; | |
| 783 request.method = "GET"; | |
| 784 request.url = GURL(full_url); | |
| 785 TestCompletionCallback callback; | |
| 786 HttpResponseInfo response; | |
| 787 HttpRequestHeaders headers; | |
| 788 NetLogWithSource net_log; | |
| 789 std::unique_ptr<SpdyHttpStream> http_stream( | |
| 790 new SpdyHttpStream(session_, true, net_log.source())); | |
| 791 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 792 CompletionCallback()), | |
| 793 IsOk()); | |
| 794 | |
| 795 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
| 796 IsError(ERR_IO_PENDING)); | |
| 797 | |
| 798 EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec()); | |
| 799 | |
| 800 callback.WaitForResult(); | |
| 801 | |
| 802 EXPECT_EQ(static_cast<int64_t>(req.size()), http_stream->GetTotalSentBytes()); | |
| 803 EXPECT_EQ(static_cast<int64_t>(resp.size()), | |
| 804 http_stream->GetTotalReceivedBytes()); | |
| 805 | |
| 806 // Because we abandoned the stream, we don't expect to find a session in the | |
| 807 // pool anymore. | |
| 808 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 809 } | |
| 810 | |
| 811 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be | |
| 812 // made available is handled correctly. | |
| 813 TEST_F(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) { | |
| 814 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 815 SpdySerializedFrame chunk1(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 816 MockWrite writes[] = { | |
| 817 CreateMockWrite(req, 0), CreateMockWrite(chunk1, 1), | |
| 818 }; | |
| 819 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 820 SpdySerializedFrame window_update( | |
| 821 spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize)); | |
| 822 MockRead reads[] = { | |
| 823 CreateMockRead(window_update, 2), MockRead(ASYNC, ERR_IO_PENDING, 3), | |
| 824 CreateMockRead(resp, 4), CreateMockRead(chunk1, 5), | |
| 825 MockRead(ASYNC, 0, 6) // EOF | |
| 826 }; | |
| 827 | |
| 828 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 829 | |
| 830 ChunkedUploadDataStream upload_stream(0); | |
| 831 | |
| 832 HttpRequestInfo request; | |
| 833 request.method = "POST"; | |
| 834 request.url = url_; | |
| 835 request.upload_data_stream = &upload_stream; | |
| 836 | |
| 837 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
| 838 NetLogWithSource()), | |
| 839 IsOk()); | |
| 840 | |
| 841 NetLogWithSource net_log; | |
| 842 std::unique_ptr<SpdyHttpStream> http_stream( | |
| 843 new SpdyHttpStream(session_, true, net_log.source())); | |
| 844 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 845 CompletionCallback()), | |
| 846 IsOk()); | |
| 847 | |
| 848 HttpRequestHeaders headers; | |
| 849 HttpResponseInfo response; | |
| 850 // This will attempt to Write() the initial request and headers, which will | |
| 851 // complete asynchronously. | |
| 852 TestCompletionCallback callback; | |
| 853 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
| 854 IsError(ERR_IO_PENDING)); | |
| 855 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 856 | |
| 857 // Complete the initial request write and first chunk. | |
| 858 base::RunLoop().RunUntilIdle(); | |
| 859 ASSERT_FALSE(callback.have_result()); | |
| 860 | |
| 861 EXPECT_EQ(static_cast<int64_t>(req.size()), http_stream->GetTotalSentBytes()); | |
| 862 EXPECT_EQ(0, http_stream->GetTotalReceivedBytes()); | |
| 863 | |
| 864 upload_stream.AppendData(kUploadData, kUploadDataSize, true); | |
| 865 | |
| 866 // Verify that the window size has decreased. | |
| 867 ASSERT_TRUE(http_stream->stream() != nullptr); | |
| 868 EXPECT_NE(static_cast<int>(kDefaultInitialWindowSize), | |
| 869 http_stream->stream()->send_window_size()); | |
| 870 | |
| 871 // Read window update. | |
| 872 base::RunLoop().RunUntilIdle(); | |
| 873 | |
| 874 ASSERT_TRUE(callback.have_result()); | |
| 875 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
| 876 | |
| 877 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk1.size()), | |
| 878 http_stream->GetTotalSentBytes()); | |
| 879 // The window update is not counted in the total received bytes. | |
| 880 EXPECT_EQ(0, http_stream->GetTotalReceivedBytes()); | |
| 881 | |
| 882 // Verify the window update. | |
| 883 ASSERT_TRUE(http_stream->stream() != nullptr); | |
| 884 EXPECT_EQ(static_cast<int>(kDefaultInitialWindowSize), | |
| 885 http_stream->stream()->send_window_size()); | |
| 886 | |
| 887 // Read rest of data. | |
| 888 sequenced_data_->Resume(); | |
| 889 base::RunLoop().RunUntilIdle(); | |
| 890 | |
| 891 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk1.size()), | |
| 892 http_stream->GetTotalSentBytes()); | |
| 893 EXPECT_EQ(static_cast<int64_t>(resp.size() + chunk1.size()), | |
| 894 http_stream->GetTotalReceivedBytes()); | |
| 895 | |
| 896 // Check response headers. | |
| 897 ASSERT_THAT(http_stream->ReadResponseHeaders(callback.callback()), IsOk()); | |
| 898 | |
| 899 // Check |chunk1| response. | |
| 900 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); | |
| 901 ASSERT_EQ(kUploadDataSize, | |
| 902 http_stream->ReadResponseBody( | |
| 903 buf1.get(), kUploadDataSize, callback.callback())); | |
| 904 EXPECT_EQ(kUploadData, SpdyString(buf1->data(), kUploadDataSize)); | |
| 905 | |
| 906 ASSERT_TRUE(response.headers.get()); | |
| 907 ASSERT_EQ(200, response.headers->response_code()); | |
| 908 } | |
| 909 | |
| 910 TEST_F(SpdyHttpStreamTest, DataReadErrorSynchronous) { | |
| 911 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 912 | |
| 913 // Server receives ERROR_CODE_INTERNAL_ERROR on client's internal failure. | |
| 914 // The failure is a reading error in this case caused by | |
| 915 // UploadDataStream::Read(). | |
| 916 SpdySerializedFrame rst_frame( | |
| 917 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_INTERNAL_ERROR)); | |
| 918 | |
| 919 MockWrite writes[] = { | |
| 920 CreateMockWrite(req, 0, SYNCHRONOUS), // Request | |
| 921 CreateMockWrite(rst_frame, 1, SYNCHRONOUS) // Reset frame | |
| 922 }; | |
| 923 | |
| 924 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 925 | |
| 926 MockRead reads[] = { | |
| 927 CreateMockRead(resp, 2), MockRead(SYNCHRONOUS, 0, 3), | |
| 928 }; | |
| 929 | |
| 930 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 931 | |
| 932 ReadErrorUploadDataStream upload_data_stream( | |
| 933 ReadErrorUploadDataStream::FailureMode::SYNC); | |
| 934 ASSERT_THAT(upload_data_stream.Init(TestCompletionCallback().callback(), | |
| 935 NetLogWithSource()), | |
| 936 IsOk()); | |
| 937 | |
| 938 HttpRequestInfo request; | |
| 939 request.method = "POST"; | |
| 940 request.url = url_; | |
| 941 request.upload_data_stream = &upload_data_stream; | |
| 942 | |
| 943 TestCompletionCallback callback; | |
| 944 HttpResponseInfo response; | |
| 945 HttpRequestHeaders headers; | |
| 946 NetLogWithSource net_log; | |
| 947 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
| 948 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 949 CompletionCallback()), | |
| 950 IsOk()); | |
| 951 | |
| 952 int result = http_stream.SendRequest(headers, &response, callback.callback()); | |
| 953 EXPECT_THAT(callback.GetResult(result), IsError(ERR_FAILED)); | |
| 954 | |
| 955 // Run posted SpdyHttpStream::ResetStreamInternal() task. | |
| 956 base::RunLoop().RunUntilIdle(); | |
| 957 | |
| 958 // Because the server has not closed the connection yet, there shouldn't be | |
| 959 // a stream but a session in the pool | |
| 960 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 961 } | |
| 962 | |
| 963 TEST_F(SpdyHttpStreamTest, DataReadErrorAsynchronous) { | |
| 964 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 965 | |
| 966 // Server receives ERROR_CODE_INTERNAL_ERROR on client's internal failure. | |
| 967 // The failure is a reading error in this case caused by | |
| 968 // UploadDataStream::Read(). | |
| 969 SpdySerializedFrame rst_frame( | |
| 970 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_INTERNAL_ERROR)); | |
| 971 | |
| 972 MockWrite writes[] = { | |
| 973 CreateMockWrite(req, 0), // Request | |
| 974 CreateMockWrite(rst_frame, 1) // Reset frame | |
| 975 }; | |
| 976 | |
| 977 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 978 | |
| 979 MockRead reads[] = { | |
| 980 MockRead(ASYNC, 0, 2), | |
| 981 }; | |
| 982 | |
| 983 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 984 | |
| 985 ReadErrorUploadDataStream upload_data_stream( | |
| 986 ReadErrorUploadDataStream::FailureMode::ASYNC); | |
| 987 ASSERT_THAT(upload_data_stream.Init(TestCompletionCallback().callback(), | |
| 988 NetLogWithSource()), | |
| 989 IsOk()); | |
| 990 | |
| 991 HttpRequestInfo request; | |
| 992 request.method = "POST"; | |
| 993 request.url = url_; | |
| 994 request.upload_data_stream = &upload_data_stream; | |
| 995 | |
| 996 TestCompletionCallback callback; | |
| 997 HttpResponseInfo response; | |
| 998 HttpRequestHeaders headers; | |
| 999 NetLogWithSource net_log; | |
| 1000 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
| 1001 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 1002 CompletionCallback()), | |
| 1003 IsOk()); | |
| 1004 | |
| 1005 int result = http_stream.SendRequest(headers, &response, callback.callback()); | |
| 1006 EXPECT_THAT(result, IsError(ERR_IO_PENDING)); | |
| 1007 EXPECT_THAT(callback.GetResult(result), IsError(ERR_FAILED)); | |
| 1008 | |
| 1009 // Run posted SpdyHttpStream::ResetStreamInternal() task. | |
| 1010 base::RunLoop().RunUntilIdle(); | |
| 1011 | |
| 1012 // Because the server has closed the connection, there shouldn't be a session | |
| 1013 // in the pool anymore. | |
| 1014 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 1015 } | |
| 1016 | |
| 1017 // Regression test for https://crbug.com/622447. | |
| 1018 TEST_F(SpdyHttpStreamTest, RequestCallbackCancelsStream) { | |
| 1019 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 1020 SpdySerializedFrame chunk( | |
| 1021 spdy_util_.ConstructSpdyDataFrame(1, nullptr, 0, true)); | |
| 1022 SpdySerializedFrame rst( | |
| 1023 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_CANCEL)); | |
| 1024 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(chunk, 1), | |
| 1025 CreateMockWrite(rst, 2)}; | |
| 1026 MockRead reads[] = {MockRead(ASYNC, 0, 3)}; | |
| 1027 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1028 | |
| 1029 HttpRequestInfo request; | |
| 1030 request.method = "POST"; | |
| 1031 request.url = url_; | |
| 1032 ChunkedUploadDataStream upload_stream(0); | |
| 1033 request.upload_data_stream = &upload_stream; | |
| 1034 | |
| 1035 TestCompletionCallback upload_callback; | |
| 1036 ASSERT_THAT( | |
| 1037 upload_stream.Init(upload_callback.callback(), NetLogWithSource()), | |
| 1038 IsOk()); | |
| 1039 upload_stream.AppendData("", 0, true); | |
| 1040 | |
| 1041 NetLogWithSource net_log; | |
| 1042 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
| 1043 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
| 1044 CompletionCallback()), | |
| 1045 IsOk()); | |
| 1046 | |
| 1047 CancelStreamCallback callback(&http_stream); | |
| 1048 HttpRequestHeaders headers; | |
| 1049 HttpResponseInfo response; | |
| 1050 // This will attempt to Write() the initial request and headers, which will | |
| 1051 // complete asynchronously. | |
| 1052 EXPECT_THAT(http_stream.SendRequest(headers, &response, callback.callback()), | |
| 1053 IsError(ERR_IO_PENDING)); | |
| 1054 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 1055 | |
| 1056 // The callback cancels |http_stream|. | |
| 1057 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
| 1058 | |
| 1059 // Finish async network reads/writes. | |
| 1060 base::RunLoop().RunUntilIdle(); | |
| 1061 | |
| 1062 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
| 1063 } | |
| 1064 | |
| 1065 // TODO(willchan): Write a longer test for SpdyStream that exercises all | |
| 1066 // methods. | |
| 1067 | |
| 1068 } // namespace test | |
| 1069 | |
| 1070 } // namespace net | |
| OLD | NEW |