| Index: net/http/http_network_transaction_unittest.cc
|
| diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
|
| index 0d467c666b3eee3cb8c4869fa55031fd344d877b..9225256c740facb432f2dbfe6e1a751b3680c3ea 100644
|
| --- a/net/http/http_network_transaction_unittest.cc
|
| +++ b/net/http/http_network_transaction_unittest.cc
|
| @@ -10,6 +10,7 @@
|
|
|
| #include <limits>
|
| #include <memory>
|
| +#include <set>
|
| #include <string>
|
| #include <utility>
|
| #include <vector>
|
| @@ -34,6 +35,7 @@
|
| #include "net/base/load_timing_info.h"
|
| #include "net/base/load_timing_info_test_util.h"
|
| #include "net/base/net_errors.h"
|
| +#include "net/base/network_stream_throttler.h"
|
| #include "net/base/proxy_delegate.h"
|
| #include "net/base/request_priority.h"
|
| #include "net/base/test_completion_callback.h"
|
| @@ -115,6 +117,100 @@ enum TestCase {
|
| kTestCaseHTTP2PriorityDependencies
|
| };
|
|
|
| +class TestNetworkStreamThrottler : public NetworkStreamThrottler {
|
| + public:
|
| + TestNetworkStreamThrottler()
|
| + : throttle_new_requests_(false),
|
| + num_set_priority_calls_(0),
|
| + last_priority_set_(IDLE) {}
|
| +
|
| + ~TestNetworkStreamThrottler() override {
|
| + EXPECT_TRUE(outstanding_throttles_.empty());
|
| + }
|
| +
|
| + // NetworkStreamThrottler
|
| + std::unique_ptr<Throttle> CreateThrottle(Delegate* delegate,
|
| + RequestPriority priority,
|
| + bool ignore_limits) override {
|
| + std::unique_ptr<TestThrottle> test_throttle(
|
| + new TestThrottle(throttle_new_requests_, delegate, this));
|
| + outstanding_throttles_.insert(test_throttle.get());
|
| + return std::move(test_throttle);
|
| + }
|
| +
|
| + void UnthrottleAllRequests() {
|
| + std::set<TestThrottle*> outstanding_throttles_copy(outstanding_throttles_);
|
| + for (auto& throttle : outstanding_throttles_copy) {
|
| + if (throttle->IsThrottled())
|
| + throttle->Unthrottle();
|
| + }
|
| + }
|
| +
|
| + void set_throttle_new_requests(bool throttle_new_requests) {
|
| + throttle_new_requests_ = throttle_new_requests;
|
| + }
|
| +
|
| + // Includes both throttled and unthrottled throttles.
|
| + size_t num_outstanding_requests() const {
|
| + return outstanding_throttles_.size();
|
| + }
|
| +
|
| + int num_set_priority_calls() const { return num_set_priority_calls_; }
|
| + RequestPriority last_priority_set() const { return last_priority_set_; }
|
| + void set_priority_change_closure(
|
| + const base::Closure& priority_change_closure) {
|
| + priority_change_closure_ = priority_change_closure;
|
| + }
|
| +
|
| + private:
|
| + class TestThrottle : public NetworkStreamThrottler::Throttle {
|
| + public:
|
| + ~TestThrottle() override { throttler_->OnThrottleDestroyed(this); }
|
| +
|
| + // Throttle
|
| + bool IsThrottled() const override { return throttled_; }
|
| + void SetPriority(RequestPriority priority) override {
|
| + throttler_->SetPriorityCalled(priority);
|
| + }
|
| +
|
| + TestThrottle(bool throttled,
|
| + Delegate* delegate,
|
| + TestNetworkStreamThrottler* throttler)
|
| + : throttled_(throttled), delegate_(delegate), throttler_(throttler) {}
|
| +
|
| + void Unthrottle() {
|
| + EXPECT_TRUE(throttled_);
|
| +
|
| + throttled_ = false;
|
| + delegate_->OnThrottleStateChanged();
|
| + }
|
| +
|
| + bool throttled_;
|
| + Delegate* delegate_;
|
| + TestNetworkStreamThrottler* throttler_;
|
| + };
|
| +
|
| + void OnThrottleDestroyed(TestThrottle* throttle) {
|
| + outstanding_throttles_.erase(throttle);
|
| + }
|
| +
|
| + void SetPriorityCalled(RequestPriority priority) {
|
| + ++num_set_priority_calls_;
|
| + last_priority_set_ = priority;
|
| + if (!priority_change_closure_.is_null())
|
| + priority_change_closure_.Run();
|
| + }
|
| +
|
| + // Includes both throttled and unthrottled throttles.
|
| + std::set<TestThrottle*> outstanding_throttles_;
|
| + bool throttle_new_requests_;
|
| + int num_set_priority_calls_;
|
| + RequestPriority last_priority_set_;
|
| + base::Closure priority_change_closure_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TestNetworkStreamThrottler);
|
| +};
|
| +
|
| const base::string16 kBar(ASCIIToUTF16("bar"));
|
| const base::string16 kBar2(ASCIIToUTF16("bar2"));
|
| const base::string16 kBar3(ASCIIToUTF16("bar3"));
|
| @@ -260,6 +356,24 @@ std::unique_ptr<HttpNetworkSession> CreateSession(
|
| return SpdySessionDependencies::SpdyCreateSession(session_deps);
|
| }
|
|
|
| +// Note that the pointer written into |*throttler| will only be valid
|
| +// for the lifetime of the returned HttpNetworkSession.
|
| +std::unique_ptr<HttpNetworkSession> CreateSessionWithThrottler(
|
| + SpdySessionDependencies* session_deps,
|
| + TestNetworkStreamThrottler** throttler) {
|
| + std::unique_ptr<HttpNetworkSession> session(
|
| + SpdySessionDependencies::SpdyCreateSession(session_deps));
|
| +
|
| + std::unique_ptr<TestNetworkStreamThrottler> owned_throttler(
|
| + new TestNetworkStreamThrottler);
|
| + *throttler = owned_throttler.get();
|
| +
|
| + HttpNetworkSessionPeer peer(session.get());
|
| + peer.SetNetworkStreamThrottler(std::move(owned_throttler));
|
| +
|
| + return session;
|
| +}
|
| +
|
| } // namespace
|
|
|
| class HttpNetworkTransactionTest
|
| @@ -15941,6 +16055,299 @@ TEST_P(HttpNetworkTransactionTest, DisableNPN) {
|
| EXPECT_TRUE(trans.server_ssl_config_.npn_protos.empty());
|
| }
|
|
|
| +// Confirm that transactions with throttler is created in the unthrottled
|
| +// state (and stays in that state) is not blocked.
|
| +TEST_P(HttpNetworkTransactionTest, ThrottlingUnthrottled) {
|
| + TestNetworkStreamThrottler* throttler(nullptr);
|
| + std::unique_ptr<HttpNetworkSession> session(
|
| + CreateSessionWithThrottler(&session_deps_, &throttler));
|
| +
|
| + // Send a simple request and make sure it goes through.
|
| + HttpRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.example.org/");
|
| + request.load_flags = 0;
|
| +
|
| + std::unique_ptr<HttpTransaction> trans(
|
| + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
|
| +
|
| + MockWrite data_writes[] = {
|
| + MockWrite("GET / HTTP/1.1\r\n"
|
| + "Host: www.example.org\r\n"
|
| + "Connection: keep-alive\r\n\r\n"),
|
| + };
|
| + MockRead data_reads[] = {
|
| + MockRead("HTTP/1.0 200 OK\r\n\r\n"), MockRead("hello world"),
|
| + MockRead(SYNCHRONOUS, OK),
|
| + };
|
| + StaticSocketDataProvider reads(data_reads, arraysize(data_reads), data_writes,
|
| + arraysize(data_writes));
|
| + session_deps_.socket_factory->AddSocketDataProvider(&reads);
|
| +
|
| + TestCompletionCallback callback;
|
| + int rv = trans->Start(&request, callback.callback(), BoundNetLog());
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| +
|
| + rv = callback.WaitForResult();
|
| + EXPECT_EQ(OK, rv);
|
| +}
|
| +
|
| +// Confirm requests can be blocked by a throttler, and are resumed
|
| +// when the throttle is unblocked.
|
| +TEST_P(HttpNetworkTransactionTest, ThrottlingBasic) {
|
| + TestNetworkStreamThrottler* throttler(nullptr);
|
| + std::unique_ptr<HttpNetworkSession> session(
|
| + CreateSessionWithThrottler(&session_deps_, &throttler));
|
| +
|
| + // Send a simple request and make sure it goes through.
|
| + HttpRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.example.org/");
|
| + request.load_flags = 0;
|
| +
|
| + MockWrite data_writes[] = {
|
| + MockWrite("GET / HTTP/1.1\r\n"
|
| + "Host: www.example.org\r\n"
|
| + "Connection: keep-alive\r\n\r\n"),
|
| + };
|
| + MockRead data_reads[] = {
|
| + MockRead("HTTP/1.0 200 OK\r\n\r\n"), MockRead("hello world"),
|
| + MockRead(SYNCHRONOUS, OK),
|
| + };
|
| + StaticSocketDataProvider reads(data_reads, arraysize(data_reads), data_writes,
|
| + arraysize(data_writes));
|
| + session_deps_.socket_factory->AddSocketDataProvider(&reads);
|
| +
|
| + // Start a request that will be throttled at start; confirm doesn't continue.
|
| + throttler->set_throttle_new_requests(true);
|
| + std::unique_ptr<HttpTransaction> trans(
|
| + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
|
| +
|
| + TestCompletionCallback callback;
|
| + int rv = trans->Start(&request, callback.callback(), BoundNetLog());
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| + EXPECT_EQ(LOAD_STATE_THROTTLED, trans->GetLoadState());
|
| + EXPECT_FALSE(callback.have_result());
|
| +
|
| + // Confirm the request goes on to complete when unthrottled.
|
| + throttler->UnthrottleAllRequests();
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| + ASSERT_TRUE(callback.have_result());
|
| + EXPECT_EQ(OK, callback.WaitForResult());
|
| +}
|
| +
|
| +// Destroy a request while it's throttled.
|
| +TEST_P(HttpNetworkTransactionTest, ThrottlingDestruction) {
|
| + TestNetworkStreamThrottler* throttler(nullptr);
|
| + std::unique_ptr<HttpNetworkSession> session(
|
| + CreateSessionWithThrottler(&session_deps_, &throttler));
|
| +
|
| + // Send a simple request and make sure it goes through.
|
| + HttpRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.example.org/");
|
| + request.load_flags = 0;
|
| +
|
| + MockWrite data_writes[] = {
|
| + MockWrite("GET / HTTP/1.1\r\n"
|
| + "Host: www.example.org\r\n"
|
| + "Connection: keep-alive\r\n\r\n"),
|
| + };
|
| + MockRead data_reads[] = {
|
| + MockRead("HTTP/1.0 200 OK\r\n\r\n"), MockRead("hello world"),
|
| + MockRead(SYNCHRONOUS, OK),
|
| + };
|
| + StaticSocketDataProvider reads(data_reads, arraysize(data_reads), data_writes,
|
| + arraysize(data_writes));
|
| + session_deps_.socket_factory->AddSocketDataProvider(&reads);
|
| +
|
| + // Start a request that will be throttled at start; confirm doesn't continue.
|
| + throttler->set_throttle_new_requests(true);
|
| + std::unique_ptr<HttpTransaction> trans(
|
| + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
|
| +
|
| + TestCompletionCallback callback;
|
| + int rv = trans->Start(&request, callback.callback(), BoundNetLog());
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| + EXPECT_EQ(LOAD_STATE_THROTTLED, trans->GetLoadState());
|
| + EXPECT_FALSE(callback.have_result());
|
| +
|
| + EXPECT_EQ(1u, throttler->num_outstanding_requests());
|
| + trans.reset();
|
| + EXPECT_EQ(0u, throttler->num_outstanding_requests());
|
| +}
|
| +
|
| +// Confirm the throttler receives SetPriority calls.
|
| +TEST_P(HttpNetworkTransactionTest, ThrottlingPrioritySet) {
|
| + TestNetworkStreamThrottler* throttler(nullptr);
|
| + std::unique_ptr<HttpNetworkSession> session(
|
| + CreateSessionWithThrottler(&session_deps_, &throttler));
|
| +
|
| + // Send a simple request and make sure it goes through.
|
| + HttpRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.example.org/");
|
| + request.load_flags = 0;
|
| +
|
| + MockWrite data_writes[] = {
|
| + MockWrite("GET / HTTP/1.1\r\n"
|
| + "Host: www.example.org\r\n"
|
| + "Connection: keep-alive\r\n\r\n"),
|
| + };
|
| + MockRead data_reads[] = {
|
| + MockRead("HTTP/1.0 200 OK\r\n\r\n"), MockRead("hello world"),
|
| + MockRead(SYNCHRONOUS, OK),
|
| + };
|
| + StaticSocketDataProvider reads(data_reads, arraysize(data_reads), data_writes,
|
| + arraysize(data_writes));
|
| + session_deps_.socket_factory->AddSocketDataProvider(&reads);
|
| +
|
| + std::unique_ptr<HttpTransaction> trans(
|
| + new HttpNetworkTransaction(IDLE, session.get()));
|
| + throttler->set_throttle_new_requests(true);
|
| + // Start the transaction to associate a throttle with it.
|
| + TestCompletionCallback callback;
|
| + int rv = trans->Start(&request, callback.callback(), BoundNetLog());
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| +
|
| + EXPECT_EQ(0, throttler->num_set_priority_calls());
|
| + trans->SetPriority(LOW);
|
| + EXPECT_EQ(1, throttler->num_set_priority_calls());
|
| + EXPECT_EQ(LOW, throttler->last_priority_set());
|
| +
|
| + throttler->UnthrottleAllRequests();
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| + ASSERT_TRUE(callback.have_result());
|
| + EXPECT_EQ(OK, callback.WaitForResult());
|
| +}
|
| +
|
| +// Confirm that unthrottling from a SetPriority call by the
|
| +// throttler works properly.
|
| +TEST_P(HttpNetworkTransactionTest, ThrottlingPrioritySetUnthrottle) {
|
| + TestNetworkStreamThrottler* throttler(nullptr);
|
| + std::unique_ptr<HttpNetworkSession> session(
|
| + CreateSessionWithThrottler(&session_deps_, &throttler));
|
| +
|
| + // Send a simple request and make sure it goes through.
|
| + HttpRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.example.org/");
|
| + request.load_flags = 0;
|
| +
|
| + MockWrite data_writes[] = {
|
| + MockWrite("GET / HTTP/1.1\r\n"
|
| + "Host: www.example.org\r\n"
|
| + "Connection: keep-alive\r\n\r\n"),
|
| + };
|
| + MockRead data_reads[] = {
|
| + MockRead("HTTP/1.0 200 OK\r\n\r\n"), MockRead("hello world"),
|
| + MockRead(SYNCHRONOUS, OK),
|
| + };
|
| + StaticSocketDataProvider reads(data_reads, arraysize(data_reads), data_writes,
|
| + arraysize(data_writes));
|
| + session_deps_.socket_factory->AddSocketDataProvider(&reads);
|
| +
|
| + StaticSocketDataProvider reads1(data_reads, arraysize(data_reads),
|
| + data_writes, arraysize(data_writes));
|
| + session_deps_.socket_factory->AddSocketDataProvider(&reads1);
|
| +
|
| + // Start a request that will be throttled at start; confirm doesn't continue.
|
| + throttler->set_throttle_new_requests(true);
|
| + std::unique_ptr<HttpTransaction> trans(
|
| + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
|
| +
|
| + TestCompletionCallback callback;
|
| + int rv = trans->Start(&request, callback.callback(), BoundNetLog());
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| + EXPECT_EQ(LOAD_STATE_THROTTLED, trans->GetLoadState());
|
| + EXPECT_FALSE(callback.have_result());
|
| +
|
| + // Create a new request, call SetPriority on it to unthrottle,
|
| + // and make sure that allows the original request to complete.
|
| + std::unique_ptr<HttpTransaction> trans1(
|
| + new HttpNetworkTransaction(LOW, session.get()));
|
| + throttler->set_priority_change_closure(
|
| + base::Bind(&TestNetworkStreamThrottler::UnthrottleAllRequests,
|
| + base::Unretained(throttler)));
|
| +
|
| + // Start the transaction to associate a throttle with it.
|
| + TestCompletionCallback callback1;
|
| + rv = trans1->Start(&request, callback1.callback(), BoundNetLog());
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| +
|
| + trans1->SetPriority(IDLE);
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| + ASSERT_TRUE(callback.have_result());
|
| + EXPECT_EQ(OK, callback.WaitForResult());
|
| +}
|
| +
|
| +// Transaction will be destroyed when the unique_ptr goes out of scope.
|
| +void DestroyTransaction(std::unique_ptr<HttpTransaction> transaction) {}
|
| +
|
| +// Confirm that destroying a transaction from a SetPriority call by the
|
| +// throttler works properly.
|
| +TEST_P(HttpNetworkTransactionTest, ThrottlingPrioritySetDestroy) {
|
| + TestNetworkStreamThrottler* throttler(nullptr);
|
| + std::unique_ptr<HttpNetworkSession> session(
|
| + CreateSessionWithThrottler(&session_deps_, &throttler));
|
| +
|
| + // Send a simple request and make sure it goes through.
|
| + HttpRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.example.org/");
|
| + request.load_flags = 0;
|
| +
|
| + MockWrite data_writes[] = {
|
| + MockWrite("GET / HTTP/1.1\r\n"
|
| + "Host: www.example.org\r\n"
|
| + "Connection: keep-alive\r\n\r\n"),
|
| + };
|
| + MockRead data_reads[] = {
|
| + MockRead("HTTP/1.0 200 OK\r\n\r\n"), MockRead("hello world"),
|
| + MockRead(SYNCHRONOUS, OK),
|
| + };
|
| + StaticSocketDataProvider reads(data_reads, arraysize(data_reads), data_writes,
|
| + arraysize(data_writes));
|
| + session_deps_.socket_factory->AddSocketDataProvider(&reads);
|
| +
|
| + StaticSocketDataProvider reads1(data_reads, arraysize(data_reads),
|
| + data_writes, arraysize(data_writes));
|
| + session_deps_.socket_factory->AddSocketDataProvider(&reads1);
|
| +
|
| + // Start a request that will be throttled at start; confirm doesn't continue.
|
| + throttler->set_throttle_new_requests(true);
|
| + std::unique_ptr<HttpTransaction> trans(
|
| + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
|
| +
|
| + TestCompletionCallback callback;
|
| + int rv = trans->Start(&request, callback.callback(), BoundNetLog());
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| + EXPECT_EQ(LOAD_STATE_THROTTLED, trans->GetLoadState());
|
| + EXPECT_FALSE(callback.have_result());
|
| +
|
| + // Arrange fo the set priority call on the above transaction to delete
|
| + // the transaction.
|
| + HttpTransaction* trans_p(trans.get());
|
| + throttler->set_priority_change_closure(
|
| + base::Bind(&DestroyTransaction, base::Passed(&trans)));
|
| +
|
| + // Call it and check results (partially a "doesn't crash" test).
|
| + trans_p->SetPriority(IDLE);
|
| + trans_p = nullptr; // No longer a valid pointer.
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| + ASSERT_FALSE(callback.have_result());
|
| +}
|
| +
|
| #if !defined(OS_IOS)
|
| TEST_P(HttpNetworkTransactionTest, TokenBindingSpdy) {
|
| const std::string https_url = "https://www.example.com";
|
|
|