// Copyright 2017 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/reporting/reporting_uploader.h" #include #include #include #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/run_loop.h" #include "base/test/scoped_feature_list.h" #include "net/base/features.h" #include "net/base/network_anonymization_key.h" #include "net/base/schemeful_site.h" #include "net/cookies/cookie_access_result.h" #include "net/cookies/cookie_store.h" #include "net/cookies/cookie_store_test_callbacks.h" #include "net/http/http_status_code.h" #include "net/socket/socket_test_util.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_response.h" #include "net/test/test_with_task_environment.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #include "url/origin.h" namespace net { namespace { class ReportingUploaderTest : public TestWithTaskEnvironment { protected: ReportingUploaderTest() : server_(test_server::EmbeddedTestServer::TYPE_HTTPS), context_(CreateTestURLRequestContextBuilder()->Build()), uploader_(ReportingUploader::Create(context_.get())) {} test_server::EmbeddedTestServer server_; std::unique_ptr context_; std::unique_ptr uploader_; const url::Origin kOrigin = url::Origin::Create(GURL("https://origin/")); }; const char kUploadBody[] = "{}"; void CheckUpload(const test_server::HttpRequest& request) { if (request.method_string != "POST") { return; } auto it = request.headers.find("Content-Type"); EXPECT_TRUE(it != request.headers.end()); EXPECT_EQ("application/reports+json", it->second); EXPECT_TRUE(request.has_content); EXPECT_EQ(kUploadBody, request.content); } std::unique_ptr AllowPreflight( const test_server::HttpRequest& request) { if (request.method_string != "OPTIONS") { return nullptr; } auto it = request.headers.find("Origin"); EXPECT_TRUE(it != request.headers.end()); auto response = std::make_unique(); response->AddCustomHeader("Access-Control-Allow-Origin", it->second); response->AddCustomHeader("Access-Control-Allow-Methods", "POST"); response->AddCustomHeader("Access-Control-Allow-Headers", "Content-Type"); response->set_code(HTTP_OK); response->set_content(""); response->set_content_type("text/plain"); return std::move(response); } std::unique_ptr ReturnResponse( HttpStatusCode code, const test_server::HttpRequest& request) { auto response = std::make_unique(); response->set_code(code); response->set_content(""); response->set_content_type("text/plain"); return std::move(response); } std::unique_ptr ReturnInvalidResponse( const test_server::HttpRequest& request) { return std::make_unique( "", "Not a valid HTTP response."); } class TestUploadCallback { public: TestUploadCallback() = default; ReportingUploader::UploadCallback callback() { return base::BindOnce(&TestUploadCallback::OnUploadComplete, base::Unretained(this)); } void WaitForCall() { if (called_) return; base::RunLoop run_loop; waiting_ = true; closure_ = run_loop.QuitClosure(); run_loop.Run(); } ReportingUploader::Outcome outcome() const { return outcome_; } private: void OnUploadComplete(ReportingUploader::Outcome outcome) { EXPECT_FALSE(called_); called_ = true; outcome_ = outcome; if (waiting_) { waiting_ = false; std::move(closure_).Run(); } } bool called_ = false; ReportingUploader::Outcome outcome_; bool waiting_ = false; base::OnceClosure closure_; }; TEST_F(ReportingUploaderTest, Upload) { server_.RegisterRequestMonitor(base::BindRepeating(&CheckUpload)); server_.RegisterRequestHandler(base::BindRepeating(&AllowPreflight)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnResponse, HTTP_OK)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); } TEST_F(ReportingUploaderTest, Success) { server_.RegisterRequestHandler(base::BindRepeating(&AllowPreflight)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnResponse, HTTP_OK)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback.outcome()); } TEST_F(ReportingUploaderTest, NetworkError1) { ASSERT_TRUE(server_.Start()); GURL url = server_.GetURL("/"); ASSERT_TRUE(server_.ShutdownAndWaitUntilComplete()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, url, IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome()); } TEST_F(ReportingUploaderTest, NetworkError2) { server_.RegisterRequestHandler(base::BindRepeating(&AllowPreflight)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnInvalidResponse)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome()); } TEST_F(ReportingUploaderTest, ServerError) { server_.RegisterRequestHandler(base::BindRepeating(&AllowPreflight)); server_.RegisterRequestHandler( base::BindRepeating(&ReturnResponse, HTTP_INTERNAL_SERVER_ERROR)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome()); } std::unique_ptr VerifyPreflight( bool* preflight_received_out, const test_server::HttpRequest& request) { if (request.method_string != "OPTIONS") { return nullptr; } *preflight_received_out = true; return AllowPreflight(request); } TEST_F(ReportingUploaderTest, VerifyPreflight) { bool preflight_received = false; server_.RegisterRequestHandler( base::BindRepeating(&VerifyPreflight, &preflight_received)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnResponse, HTTP_OK)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_TRUE(preflight_received); EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback.outcome()); } TEST_F(ReportingUploaderTest, SkipPreflightForSameOrigin) { bool preflight_received = false; server_.RegisterRequestHandler( base::BindRepeating(&VerifyPreflight, &preflight_received)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnResponse, HTTP_OK)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; auto server_origin = url::Origin::Create(server_.base_url()); uploader_->StartUpload(server_origin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_FALSE(preflight_received); EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback.outcome()); } std::unique_ptr ReturnPreflightError( const test_server::HttpRequest& request) { if (request.method_string != "OPTIONS") { return nullptr; } auto response = std::make_unique(); response->set_code(HTTP_FORBIDDEN); response->set_content(""); response->set_content_type("text/plain"); return std::move(response); } TEST_F(ReportingUploaderTest, FailedCorsPreflight) { server_.RegisterRequestHandler(base::BindRepeating(&ReturnPreflightError)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnResponse, HTTP_OK)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome()); } std::unique_ptr ReturnPreflightWithoutOrigin( const test_server::HttpRequest& request) { if (request.method_string != "OPTIONS") { return nullptr; } auto it = request.headers.find("Origin"); EXPECT_TRUE(it != request.headers.end()); auto response = std::make_unique(); response->AddCustomHeader("Access-Control-Allow-Methods", "POST"); response->AddCustomHeader("Access-Control-Allow-Headers", "Content-Type"); response->set_code(HTTP_OK); response->set_content(""); response->set_content_type("text/plain"); return std::move(response); } TEST_F(ReportingUploaderTest, CorsPreflightWithoutOrigin) { server_.RegisterRequestHandler( base::BindRepeating(&ReturnPreflightWithoutOrigin)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnResponse, HTTP_OK)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome()); } std::unique_ptr ReturnPreflightWithoutMethods( const test_server::HttpRequest& request) { if (request.method_string != "OPTIONS") { return nullptr; } auto it = request.headers.find("Origin"); EXPECT_TRUE(it != request.headers.end()); auto response = std::make_unique(); response->AddCustomHeader("Access-Control-Allow-Origin", it->second); response->AddCustomHeader("Access-Control-Allow-Headers", "Content-Type"); response->set_code(HTTP_OK); response->set_content(""); response->set_content_type("text/plain"); return std::move(response); } TEST_F(ReportingUploaderTest, CorsPreflightWithoutMethods) { server_.RegisterRequestHandler( base::BindRepeating(&ReturnPreflightWithoutMethods)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnResponse, HTTP_OK)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback.outcome()); } std::unique_ptr ReturnPreflightWithWildcardMethods( const test_server::HttpRequest& request) { if (request.method_string != "OPTIONS") { return nullptr; } auto it = request.headers.find("Origin"); EXPECT_TRUE(it != request.headers.end()); auto response = std::make_unique(); response->AddCustomHeader("Access-Control-Allow-Origin", it->second); response->AddCustomHeader("Access-Control-Allow-Headers", "Content-Type"); response->AddCustomHeader("Access-Control-Allow-Methods", "*"); response->set_code(HTTP_OK); response->set_content(""); response->set_content_type("text/plain"); return std::move(response); } TEST_F(ReportingUploaderTest, CorsPreflightWildcardMethods) { server_.RegisterRequestHandler( base::BindRepeating(&ReturnPreflightWithWildcardMethods)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnResponse, HTTP_OK)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback.outcome()); } std::unique_ptr ReturnPreflightWithoutHeaders( const test_server::HttpRequest& request) { if (request.method_string != "OPTIONS") { return nullptr; } auto it = request.headers.find("Origin"); EXPECT_TRUE(it != request.headers.end()); auto response = std::make_unique(); response->AddCustomHeader("Access-Control-Allow-Origin", it->second); response->AddCustomHeader("Access-Control-Allow-Methods", "POST"); response->set_code(HTTP_OK); response->set_content(""); response->set_content_type("text/plain"); return std::move(response); } TEST_F(ReportingUploaderTest, CorsPreflightWithoutHeaders) { server_.RegisterRequestHandler( base::BindRepeating(&ReturnPreflightWithoutHeaders)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnResponse, HTTP_OK)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome()); } std::unique_ptr ReturnPreflightWithWildcardHeaders( const test_server::HttpRequest& request) { if (request.method_string != "OPTIONS") { return nullptr; } auto it = request.headers.find("Origin"); EXPECT_TRUE(it != request.headers.end()); auto response = std::make_unique(); response->AddCustomHeader("Access-Control-Allow-Origin", it->second); response->AddCustomHeader("Access-Control-Allow-Headers", "*"); response->AddCustomHeader("Access-Control-Allow-Methods", "POST"); response->set_code(HTTP_OK); response->set_content(""); response->set_content_type("text/plain"); return std::move(response); } TEST_F(ReportingUploaderTest, CorsPreflightWildcardHeaders) { server_.RegisterRequestHandler( base::BindRepeating(&ReturnPreflightWithWildcardHeaders)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnResponse, HTTP_OK)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback.outcome()); } TEST_F(ReportingUploaderTest, RemoveEndpoint) { server_.RegisterRequestHandler(base::BindRepeating(&AllowPreflight)); server_.RegisterRequestHandler( base::BindRepeating(&ReturnResponse, HTTP_GONE)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::REMOVE_ENDPOINT, callback.outcome()); } const char kRedirectPath[] = "/redirect"; std::unique_ptr ReturnRedirect( const std::string& location, const test_server::HttpRequest& request) { if (request.relative_url != "/") return nullptr; auto response = std::make_unique(); response->set_code(HTTP_FOUND); response->AddCustomHeader("Location", location); response->set_content( "Thank you, Mario! But our Princess is in another castle."); response->set_content_type("text/plain"); return std::move(response); } std::unique_ptr CheckRedirect( bool* redirect_followed_out, const test_server::HttpRequest& request) { if (request.relative_url != kRedirectPath) return nullptr; *redirect_followed_out = true; return ReturnResponse(HTTP_OK, request); } TEST_F(ReportingUploaderTest, FollowHttpsRedirect) { bool followed = false; server_.RegisterRequestHandler(base::BindRepeating(&AllowPreflight)); server_.RegisterRequestHandler( base::BindRepeating(&ReturnRedirect, kRedirectPath)); server_.RegisterRequestHandler( base::BindRepeating(&CheckRedirect, &followed)); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_TRUE(followed); EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback.outcome()); } TEST_F(ReportingUploaderTest, DontFollowHttpRedirect) { bool followed = false; test_server::EmbeddedTestServer http_server_; http_server_.RegisterRequestHandler( base::BindRepeating(&CheckRedirect, &followed)); ASSERT_TRUE(http_server_.Start()); const GURL target = http_server_.GetURL(kRedirectPath); server_.RegisterRequestHandler(base::BindRepeating(&AllowPreflight)); server_.RegisterRequestHandler( base::BindRepeating(&ReturnRedirect, target.spec())); ASSERT_TRUE(server_.Start()); TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); EXPECT_FALSE(followed); EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome()); } void CheckNoCookie(const test_server::HttpRequest& request) { auto it = request.headers.find("Cookie"); EXPECT_TRUE(it == request.headers.end()); } TEST_F(ReportingUploaderTest, DontSendCookies) { server_.RegisterRequestMonitor(base::BindRepeating(&CheckNoCookie)); server_.RegisterRequestHandler(base::BindRepeating(&AllowPreflight)); server_.RegisterRequestHandler(base::BindRepeating(&ReturnResponse, HTTP_OK)); ASSERT_TRUE(server_.Start()); ResultSavingCookieCallback cookie_callback; GURL url = server_.GetURL("/"); auto cookie = CanonicalCookie::Create( url, "foo=bar", base::Time::Now(), absl::nullopt /* server_time */, absl::nullopt /* cookie_partition_key */); context_->cookie_store()->SetCanonicalCookieAsync( std::move(cookie), url, CookieOptions::MakeAllInclusive(), cookie_callback.MakeCallback()); cookie_callback.WaitUntilDone(); ASSERT_TRUE(cookie_callback.result().status.IsInclude()); TestUploadCallback upload_callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, upload_callback.callback()); upload_callback.WaitForCall(); } std::unique_ptr SendCookie( const test_server::HttpRequest& request) { auto response = std::make_unique(); response->set_code(HTTP_OK); response->AddCustomHeader("Set-Cookie", "foo=bar"); response->set_content(""); response->set_content_type("text/plain"); return std::move(response); } TEST_F(ReportingUploaderTest, DontSaveCookies) { server_.RegisterRequestHandler(base::BindRepeating(&AllowPreflight)); server_.RegisterRequestHandler(base::BindRepeating(&SendCookie)); ASSERT_TRUE(server_.Start()); TestUploadCallback upload_callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, upload_callback.callback()); upload_callback.WaitForCall(); GetCookieListCallback cookie_callback; context_->cookie_store()->GetCookieListWithOptionsAsync( server_.GetURL("/"), CookieOptions::MakeAllInclusive(), CookiePartitionKeyCollection(), base::BindOnce(&GetCookieListCallback::Run, base::Unretained(&cookie_callback))); cookie_callback.WaitUntilDone(); EXPECT_TRUE(cookie_callback.cookies().empty()); } std::unique_ptr ReturnCacheableResponse( int* request_count_out, const test_server::HttpRequest& request) { ++*request_count_out; auto response = std::make_unique(); response->set_code(HTTP_OK); response->AddCustomHeader("Cache-Control", "max-age=86400"); response->set_content(""); response->set_content_type("text/plain"); return std::move(response); } // TODO(juliatuttle): This passes even if the uploader doesn't set // LOAD_DISABLE_CACHE. Maybe that's okay -- Chromium might not cache POST // responses ever -- but this test should either not exist or be sure that it is // testing actual functionality, not a default. TEST_F(ReportingUploaderTest, DontCacheResponse) { int request_count = 0; server_.RegisterRequestHandler(base::BindRepeating(&AllowPreflight)); server_.RegisterRequestHandler( base::BindRepeating(&ReturnCacheableResponse, &request_count)); ASSERT_TRUE(server_.Start()); { TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); } EXPECT_EQ(1, request_count); { TestUploadCallback callback; uploader_->StartUpload(kOrigin, server_.GetURL("/"), IsolationInfo::CreateTransient(), kUploadBody, 0, false, callback.callback()); callback.WaitForCall(); } EXPECT_EQ(2, request_count); } // Create two requests with the same NetworkAnonymizationKey, and one request // with a different one, and make sure only the requests with the same // NetworkAnonymizationKey share a socket. TEST_F(ReportingUploaderTest, RespectsNetworkAnonymizationKey) { // While network state partitioning is not needed for reporting code to // respect NetworkAnonymizationKey, this test works by ensuring that // Reporting's NetworkAnonymizationKey makes it to the socket pool layer and // is respected there, so this test needs to enable // network state partitioning. base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( features::kPartitionConnectionsByNetworkIsolationKey); const SchemefulSite kSite1 = SchemefulSite(kOrigin); const SchemefulSite kSite2(GURL("https://origin2/")); ASSERT_NE(kSite1, kSite2); const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1); const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2); const url::Origin kSiteOrigin1 = url::Origin::Create(kSite1.GetURL()); const url::Origin kSiteOrigin2 = url::Origin::Create(kSite2.GetURL()); const IsolationInfo kIsolationInfo1 = IsolationInfo::Create(net::IsolationInfo::RequestType::kOther, kSiteOrigin1, kSiteOrigin1, net::SiteForCookies()); const IsolationInfo kIsolationInfo2 = IsolationInfo::Create(net::IsolationInfo::RequestType::kOther, kSiteOrigin2, kSiteOrigin2, net::SiteForCookies()); MockClientSocketFactory socket_factory; auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_client_socket_factory_for_testing(&socket_factory); auto context = context_builder->Build(); // First socket handles first and third requests. MockWrite writes1[] = { MockWrite(SYNCHRONOUS, 0, "POST /1 HTTP/1.1\r\n" "Host: origin\r\n" "Connection: keep-alive\r\n" "Content-Length: 2\r\n" "Content-Type: application/reports+json\r\n" "User-Agent: \r\n" "Accept-Encoding: gzip, deflate\r\n" "Accept-Language: en-us,fr\r\n\r\n"), MockWrite(SYNCHRONOUS, 1, kUploadBody), MockWrite(SYNCHRONOUS, 3, "POST /3 HTTP/1.1\r\n" "Host: origin\r\n" "Connection: keep-alive\r\n" "Content-Length: 2\r\n" "Content-Type: application/reports+json\r\n" "User-Agent: \r\n" "Accept-Encoding: gzip, deflate\r\n" "Accept-Language: en-us,fr\r\n\r\n"), MockWrite(SYNCHRONOUS, 4, kUploadBody), }; MockRead reads1[] = { MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n" "Connection: Keep-Alive\r\n" "Content-Length: 0\r\n\r\n"), MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n" "Connection: Keep-Alive\r\n" "Content-Length: 0\r\n\r\n"), }; SequencedSocketData data1(reads1, writes1); socket_factory.AddSocketDataProvider(&data1); SSLSocketDataProvider ssl_data1(ASYNC, OK); socket_factory.AddSSLSocketDataProvider(&ssl_data1); // Second socket handles second request. MockWrite writes2[] = { MockWrite(SYNCHRONOUS, 0, "POST /2 HTTP/1.1\r\n" "Host: origin\r\n" "Connection: keep-alive\r\n" "Content-Length: 2\r\n" "Content-Type: application/reports+json\r\n" "User-Agent: \r\n" "Accept-Encoding: gzip, deflate\r\n" "Accept-Language: en-us,fr\r\n\r\n"), MockWrite(SYNCHRONOUS, 1, kUploadBody), }; MockRead reads2[] = { MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n" "Connection: Keep-Alive\r\n" "Content-Length: 0\r\n\r\n"), }; SequencedSocketData data2(reads2, writes2); socket_factory.AddSocketDataProvider(&data2); SSLSocketDataProvider ssl_data2(ASYNC, OK); socket_factory.AddSSLSocketDataProvider(&ssl_data2); TestUploadCallback callback1; std::unique_ptr uploader1 = ReportingUploader::Create(context.get()); uploader1->StartUpload(kOrigin, GURL("https://origin/1"), kIsolationInfo1, kUploadBody, 0, false, callback1.callback()); callback1.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback1.outcome()); // Start two more requests in parallel. The first started uses a different // NetworkAnonymizationKey, so should create a new socket, while the second // one gets the other socket. Start in parallel to make sure that a new socket // isn't created just because the first is returned to the socket pool // asynchronously. TestUploadCallback callback2; std::unique_ptr uploader2 = ReportingUploader::Create(context.get()); uploader2->StartUpload(kOrigin, GURL("https://origin/2"), kIsolationInfo2, kUploadBody, 0, false, callback2.callback()); TestUploadCallback callback3; std::unique_ptr uploader3 = ReportingUploader::Create(context.get()); uploader3->StartUpload(kOrigin, GURL("https://origin/3"), kIsolationInfo1, kUploadBody, 0, false, callback3.callback()); callback2.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback2.outcome()); callback3.WaitForCall(); EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback3.outcome()); } } // namespace } // namespace net