Index: ios/web/net/request_tracker_impl_unittest.mm |
diff --git a/ios/web/net/request_tracker_impl_unittest.mm b/ios/web/net/request_tracker_impl_unittest.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..bb87192c991b1d07f8fada42238e37ce61159221 |
--- /dev/null |
+++ b/ios/web/net/request_tracker_impl_unittest.mm |
@@ -0,0 +1,498 @@ |
+// Copyright 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ios/web/net/request_tracker_impl.h" |
+ |
+#include "base/logging.h" |
+#include "base/mac/scoped_nsobject.h" |
+#include "base/memory/scoped_vector.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "ios/web/public/cert_policy.h" |
+#include "ios/web/public/certificate_policy_cache.h" |
+#include "ios/web/public/ssl_status.h" |
+#include "ios/web/public/test/test_browser_state.h" |
+#include "ios/web/public/test/test_web_thread.h" |
+#include "net/cert/x509_certificate.h" |
+#include "net/http/http_response_headers.h" |
+#include "net/url_request/url_request.h" |
+#include "net/url_request/url_request_context.h" |
+#include "net/url_request/url_request_job_factory.h" |
+#include "net/url_request/url_request_job_factory_impl.h" |
+#include "net/url_request/url_request_test_job.h" |
+#include "net/url_request/url_request_test_util.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "testing/gtest_mac.h" |
+#include "testing/platform_test.h" |
+#import "third_party/ocmock/OCMock/OCMock.h" |
+#include "third_party/ocmock/gtest_support.h" |
+ |
+@interface RequestTrackerNotificationReceiverTest |
+ : NSObject<CRWRequestTrackerDelegate> { |
+ @public |
+ float value_; |
+ float max_; |
+ @private |
+ base::scoped_nsobject<NSString> error_; |
+ scoped_refptr<net::HttpResponseHeaders> headers_; |
+} |
+ |
+- (NSString*)error; |
+- (net::HttpResponseHeaders*)headers; |
+@end |
+ |
+@implementation RequestTrackerNotificationReceiverTest |
+ |
+- (id)init { |
+ self = [super init]; |
+ if (self) { |
+ value_ = 0.0f; |
+ max_ = 0.0f; |
+ } |
+ return self; |
+} |
+ |
+- (BOOL)isForStaticFileRequests { |
+ return NO; |
+} |
+ |
+- (void)updatedProgress:(float)progress { |
+ if (progress > 0.0f) { |
+ if (progress < value_) { |
+ error_.reset( |
+ [[NSString stringWithFormat: |
+ @"going down from %f to %f", value_, progress] retain]); |
+ } |
+ value_ = progress; |
+ } else { |
+ value_ = 0.0f; |
+ } |
+ if (value_ > max_) { |
+ max_ = value_; |
+ } |
+} |
+ |
+- (NSString*)error { |
+ return error_; |
+} |
+ |
+- (void)handleResponseHeaders:(net::HttpResponseHeaders*)headers |
+ requestUrl:(const GURL&)requestUrl { |
+ headers_ = headers; |
+} |
+ |
+- (net::HttpResponseHeaders*)headers { |
+ return headers_.get(); |
+} |
+ |
+- (void)updatedSSLStatus:(const web::SSLStatus&)sslStatus |
+ forPageUrl:(const GURL&)url |
+ userInfo:(id)userInfo { |
+ // Nothing. yet. |
+} |
+ |
+- (void)presentSSLError:(const net::SSLInfo&)info |
+ forSSLStatus:(const web::SSLStatus&)status |
+ onUrl:(const GURL&)url |
+ recoverable:(BOOL)recoverable |
+ callback:(SSLErrorCallback)shouldContinue { |
+ // Nothing, yet. |
+} |
+ |
+- (void)certificateUsed:(net::X509Certificate*)certificate |
+ forHost:(const std::string&)host |
+ status:(net::CertStatus)status { |
+ // Nothing, yet. |
+} |
+ |
+- (void)clearCertificates { |
+ // Nothing, yet. |
+} |
+ |
+- (void)handlePassKitObject:(NSData*)data { |
+ // Nothing yet. |
+} |
+ |
+@end |
+ |
+namespace { |
+ |
+// Used and incremented each time a tabId is created. |
+int g_count = 0; |
+ |
+class RequestTrackerTest : public PlatformTest { |
+ public: |
+ RequestTrackerTest() |
+ : loop_(base::MessageLoop::TYPE_IO), |
+ ui_thread_(web::WebThread::UI, &loop_), |
+ io_thread_(web::WebThread::IO, &loop_){}; |
+ |
+ ~RequestTrackerTest() override {} |
+ |
+ void SetUp() override { |
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
+ request_group_id_.reset( |
+ [[NSString stringWithFormat:@"test%d", g_count++] retain]); |
+ |
+ receiver_.reset([[RequestTrackerNotificationReceiverTest alloc] init]); |
+ tracker_ = web::RequestTrackerImpl::CreateTrackerForRequestGroupID( |
+ request_group_id_, |
+ &browser_state_, |
+ browser_state_.GetRequestContext(), |
+ receiver_); |
+ } |
+ |
+ void TearDown() override { |
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
+ tracker_->Close(); |
+ } |
+ |
+ base::MessageLoop loop_; |
+ web::TestWebThread ui_thread_; |
+ web::TestWebThread io_thread_; |
+ |
+ base::scoped_nsobject<RequestTrackerNotificationReceiverTest> receiver_; |
+ scoped_refptr<web::RequestTrackerImpl> tracker_; |
+ base::scoped_nsobject<NSString> request_group_id_; |
+ web::TestBrowserState browser_state_; |
+ ScopedVector<net::URLRequestContext> contexts_; |
+ ScopedVector<net::URLRequest> requests_; |
+ net::URLRequestJobFactoryImpl job_factory_; |
+ |
+ GURL GetURL(size_t i) { |
+ std::stringstream ss; |
+ ss << "http://www/"; |
+ ss << i; |
+ return GURL(ss.str()); |
+ } |
+ |
+ GURL GetSecureURL(size_t i) { |
+ std::stringstream ss; |
+ ss << "https://www/"; |
+ ss << i; |
+ return GURL(ss.str()); |
+ } |
+ |
+ net::URLRequest* GetRequest(size_t i) { |
+ return GetInternalRequest(i, false); |
+ } |
+ |
+ net::URLRequest* GetSecureRequest(size_t i) { |
+ return GetInternalRequest(i, true); |
+ } |
+ |
+ NSString* WaitUntilLoop(bool (^condition)(void)) { |
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
+ base::Time maxDate = base::Time::Now() + base::TimeDelta::FromSeconds(10); |
+ while (!condition()) { |
+ if ([receiver_ error]) |
+ return [receiver_ error]; |
+ if (base::Time::Now() > maxDate) |
+ return @"Time is up, too slow to go"; |
+ loop_.RunUntilIdle(); |
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1)); |
+ } |
+ return nil; |
+ } |
+ |
+ NSString* CheckActive() { |
+ NSString* message = WaitUntilLoop(^{ |
+ return (receiver_.get()->value_ > 0.0f); |
+ }); |
+ |
+ if (!message && (receiver_.get()->max_ == 0.0f)) |
+ message = @"Max should be greater than 0.0"; |
+ return message; |
+ } |
+ |
+ void TrimRequest(NSString* tab_id, const GURL& url) { |
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
+ receiver_.get()->value_ = 0.0f; |
+ receiver_.get()->max_ = 0.0f; |
+ tracker_->StartPageLoad(url, nil); |
+ } |
+ |
+ void EndPage(NSString* tab_id, const GURL& url) { |
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
+ tracker_->FinishPageLoad(url, false); |
+ receiver_.get()->value_ = 0.0f; |
+ receiver_.get()->max_ = 0.0f; |
+ loop_.RunUntilIdle(); |
+ } |
+ |
+ net::TestJobInterceptor* AddInterceptorToRequest(size_t i) { |
+ // |interceptor| will be deleted from |job_factory_|'s destructor. |
+ net::TestJobInterceptor* protocol_handler = new net::TestJobInterceptor(); |
+ job_factory_.SetProtocolHandler("http", protocol_handler); |
+ contexts_[i]->set_job_factory(&job_factory_); |
+ return protocol_handler; |
+ } |
+ |
+ private: |
+ net::URLRequest* GetInternalRequest(size_t i, bool secure) { |
+ GURL url; |
+ if (secure) |
+ url = GetSecureURL(requests_.size()); |
+ else |
+ url = GetURL(requests_.size()); |
+ |
+ while (i >= requests_.size()) { |
+ contexts_.push_back(new net::URLRequestContext()); |
+ requests_.push_back(contexts_[i]->CreateRequest(url, |
+ net::DEFAULT_PRIORITY, |
+ NULL).release()); |
+ |
+ if (secure) { |
+ // Put a valid SSLInfo inside |
+ net::HttpResponseInfo* response = |
+ const_cast<net::HttpResponseInfo*>(&requests_[i]->response_info()); |
+ |
+ response->ssl_info.cert = new net::X509Certificate( |
+ "subject", "issuer", |
+ base::Time::Now() - base::TimeDelta::FromDays(2), |
+ base::Time::Now() + base::TimeDelta::FromDays(2)); |
+ response->ssl_info.cert_status = 0; // No errors. |
+ response->ssl_info.security_bits = 128; |
+ |
+ EXPECT_TRUE(requests_[i]->ssl_info().is_valid()); |
+ } |
+ } |
+ EXPECT_TRUE(!secure == !requests_[i]->url().SchemeIsSecure()); |
+ return requests_[i]; |
+ } |
+ |
+ DISALLOW_COPY_AND_ASSIGN(RequestTrackerTest); |
+}; |
+ |
+TEST_F(RequestTrackerTest, OnePage) { |
+ // Start a request. |
+ tracker_->StartRequest(GetRequest(0)); |
+ // Start page load. |
+ TrimRequest(request_group_id_, GetURL(0)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ // Stop the request. |
+ tracker_->StopRequest(GetRequest(0)); |
+ EndPage(request_group_id_, GetURL(0)); |
+} |
+ |
+TEST_F(RequestTrackerTest, OneSecurePage) { |
+ net::URLRequest* request = GetSecureRequest(0); |
+ GURL url = GetSecureURL(0); |
+ |
+ // Start a page. |
+ TrimRequest(request_group_id_, url); |
+ |
+ // Start a request. |
+ tracker_->StartRequest(request); |
+ tracker_->CaptureReceivedBytes(request, 42); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ // Stop the request. |
+ tracker_->StopRequest(request); |
+ |
+ EndPage(request_group_id_, url); |
+} |
+ |
+TEST_F(RequestTrackerTest, OnePageAndResources) { |
+ // Start a page. |
+ TrimRequest(request_group_id_, GetURL(0)); |
+ // Start two requests. |
+ tracker_->StartRequest(GetRequest(0)); |
+ tracker_->StartRequest(GetRequest(1)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ tracker_->StopRequest(GetRequest(0)); |
+ tracker_->StartRequest(GetRequest(2)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ tracker_->StopRequest(GetRequest(1)); |
+ tracker_->StartRequest(GetRequest(3)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ tracker_->StopRequest(GetRequest(2)); |
+ tracker_->StartRequest(GetRequest(4)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ tracker_->StopRequest(GetRequest(3)); |
+ tracker_->StopRequest(GetRequest(4)); |
+ EndPage(request_group_id_, GetURL(0)); |
+} |
+ |
+TEST_F(RequestTrackerTest, OnePageOneBigImage) { |
+ // Start a page. |
+ TrimRequest(request_group_id_, GetURL(0)); |
+ tracker_->StartRequest(GetRequest(0)); |
+ tracker_->StopRequest(GetRequest(0)); |
+ tracker_->StartRequest(GetRequest(1)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ tracker_->CaptureReceivedBytes(GetRequest(1), 10); |
+ tracker_->CaptureExpectedLength(GetRequest(1), 100); |
+ tracker_->CaptureReceivedBytes(GetRequest(1), 10); |
+ tracker_->CaptureReceivedBytes(GetRequest(1), 10); |
+ tracker_->CaptureReceivedBytes(GetRequest(1), 10); |
+ tracker_->CaptureReceivedBytes(GetRequest(1), 10); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ tracker_->CaptureReceivedBytes(GetRequest(1), 10); |
+ tracker_->CaptureReceivedBytes(GetRequest(1), 10); |
+ tracker_->CaptureReceivedBytes(GetRequest(1), 10); |
+ tracker_->CaptureReceivedBytes(GetRequest(1), 10); |
+ tracker_->CaptureReceivedBytes(GetRequest(1), 10); |
+ tracker_->StopRequest(GetRequest(1)); |
+ EndPage(request_group_id_, GetURL(0)); |
+} |
+ |
+TEST_F(RequestTrackerTest, TwoPagesPostStart) { |
+ tracker_->StartRequest(GetRequest(0)); |
+ |
+ TrimRequest(request_group_id_, GetURL(0)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ tracker_->StartRequest(GetRequest(1)); |
+ tracker_->StartRequest(GetRequest(2)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ tracker_->StopRequest(GetRequest(0)); |
+ tracker_->StopRequest(GetRequest(1)); |
+ tracker_->StopRequest(GetRequest(2)); |
+ EndPage(request_group_id_, GetURL(0)); |
+ |
+ tracker_->StartRequest(GetRequest(3)); |
+ |
+ TrimRequest(request_group_id_, GetURL(3)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ tracker_->StopRequest(GetRequest(3)); |
+ EndPage(request_group_id_, GetURL(3)); |
+} |
+ |
+TEST_F(RequestTrackerTest, TwoPagesPreStart) { |
+ tracker_->StartRequest(GetRequest(0)); |
+ |
+ TrimRequest(request_group_id_, GetURL(0)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ tracker_->StartRequest(GetRequest(1)); |
+ tracker_->StartRequest(GetRequest(2)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ tracker_->StopRequest(GetRequest(0)); |
+ tracker_->StopRequest(GetRequest(1)); |
+ tracker_->StopRequest(GetRequest(2)); |
+ EndPage(request_group_id_, GetURL(0)); |
+ |
+ TrimRequest(request_group_id_, GetURL(3)); |
+ tracker_->StartRequest(GetRequest(3)); |
+ tracker_->StopRequest(GetRequest(3)); |
+ EndPage(request_group_id_, GetURL(3)); |
+} |
+ |
+TEST_F(RequestTrackerTest, TwoPagesNoWait) { |
+ tracker_->StartRequest(GetRequest(0)); |
+ |
+ TrimRequest(request_group_id_, GetURL(0)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ tracker_->StartRequest(GetRequest(1)); |
+ tracker_->StartRequest(GetRequest(2)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ tracker_->StopRequest(GetRequest(0)); |
+ tracker_->StopRequest(GetRequest(1)); |
+ tracker_->StopRequest(GetRequest(2)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ TrimRequest(request_group_id_, GetURL(3)); |
+ tracker_->StartRequest(GetRequest(3)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ tracker_->StopRequest(GetRequest(3)); |
+ EXPECT_NSEQ(nil, CheckActive()); |
+ |
+ EndPage(request_group_id_, GetURL(3)); |
+} |
+ |
+TEST_F(RequestTrackerTest, CaptureHeaders) { |
+ std::string headers = |
+ "HTTP/1.1 200 OK\n" |
+ "content-type: multipart/mixed; boundary=inner\n" |
+ "content-disposition: attachment; filename=\"name.pdf\"\n" |
+ "X-Auto-Login: Hello World\n\n"; |
+ for (size_t i = 0; i < headers.length(); i++) { |
+ if (headers.data()[i] == '\n') |
+ const_cast<char*>(headers.data())[i] = '\0'; |
+ } |
+ net::URLRequest* request = GetRequest(0); |
+ const_cast<net::HttpResponseInfo&>(request->response_info()).headers = |
+ new net::HttpResponseHeaders(headers); |
+ // |job| will be owned by |request| and released from its destructor. |
+ net::URLRequestTestJob* job = new net::URLRequestTestJob( |
+ request, request->context()->network_delegate(), headers, "", false); |
+ AddInterceptorToRequest(0)->set_main_intercept_job(job); |
+ request->Start(); |
+ |
+ tracker_->StartRequest(request); |
+ tracker_->CaptureHeaders(request); |
+ tracker_->StopRequest(request); |
+ loop_.RunUntilIdle(); |
+ EXPECT_TRUE([receiver_ headers]->HasHeaderValue("X-Auto-Login", |
+ "Hello World")); |
+ std::string mimeType; |
+ EXPECT_TRUE([receiver_ headers]->GetMimeType(&mimeType)); |
+ EXPECT_EQ("multipart/mixed", mimeType); |
+ EXPECT_TRUE([receiver_ headers]->HasHeaderValue( |
+ "Content-Disposition", "attachment; filename=\"name.pdf\"")); |
+} |
+ |
+// Do-nothing mock CertificatePolicyCache. Allows all certs for all hosts. |
+class MockCertificatePolicyCache : public web::CertificatePolicyCache { |
+ public: |
+ MockCertificatePolicyCache() {} |
+ |
+ void AllowCertForHost(net::X509Certificate* cert, |
+ const std::string& host, |
+ net::CertStatus error) override { |
+ } |
+ |
+ web::CertPolicy::Judgment QueryPolicy(net::X509Certificate* cert, |
+ const std::string& host, |
+ net::CertStatus error) override { |
+ return web::CertPolicy::Judgment::ALLOWED; |
+ } |
+ |
+ void ClearCertificatePolicies() override { |
+ } |
+ |
+ private: |
+ ~MockCertificatePolicyCache() override {} |
+}; |
+ |
+void TwoStartsSSLCallback(bool* called, bool ok) { |
+ *called = true; |
+} |
+ |
+// crbug/386180 |
+TEST_F(RequestTrackerTest, DISABLED_TwoStartsNoEstimate) { |
+ net::X509Certificate* cert = |
+ new net::X509Certificate("subject", "issuer", base::Time::Now(), |
+ base::Time::Max()); |
+ net::SSLInfo ssl_info; |
+ ssl_info.cert = cert; |
+ ssl_info.cert_status = net::CERT_STATUS_AUTHORITY_INVALID; |
+ scoped_refptr<MockCertificatePolicyCache> cache; |
+ tracker_->SetCertificatePolicyCacheForTest(cache.get()); |
+ TrimRequest(request_group_id_, GetSecureURL(0)); |
+ tracker_->StartRequest(GetSecureRequest(0)); |
+ tracker_->StartRequest(GetSecureRequest(1)); |
+ bool request_0_called = false; |
+ bool request_1_called = false; |
+ tracker_->OnSSLCertificateError(GetSecureRequest(0), ssl_info, true, |
+ base::Bind(&TwoStartsSSLCallback, |
+ &request_0_called)); |
+ tracker_->OnSSLCertificateError(GetSecureRequest(1), ssl_info, true, |
+ base::Bind(&TwoStartsSSLCallback, |
+ &request_1_called)); |
+ EXPECT_TRUE(request_0_called); |
+ EXPECT_TRUE(request_1_called); |
+} |
+ |
+} // namespace |