blob: 99feb490efe3abd3829de0dbb524454f43cb5e6d [file] [log] [blame]
// Copyright 2015 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.
#import <Cronet/Cronet.h>
#import <Foundation/Foundation.h>
#include <stdint.h>
#include "base/logging.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "components/cronet/ios/test/cronet_test_base.h"
#include "components/cronet/ios/test/start_cronet.h"
#include "components/cronet/ios/test/test_server.h"
#include "components/grpc_support/test/quic_test_server.h"
#include "net/base/mac/url_conversions.h"
#include "net/base/net_errors.h"
#include "net/cert/mock_cert_verifier.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "url/gurl.h"
namespace cronet {
const char kUserAgent[] = "CronetTest/1.0.0.0";
class HttpTest : public CronetTestBase {
protected:
HttpTest() {}
~HttpTest() override {}
void SetUp() override {
CronetTestBase::SetUp();
TestServer::Start();
[Cronet setRequestFilterBlock:^(NSURLRequest* request) {
return YES;
}];
StartCronet(grpc_support::GetQuicTestServerPort());
[Cronet registerHttpProtocolHandler];
NSURLSessionConfiguration* config =
[NSURLSessionConfiguration ephemeralSessionConfiguration];
[Cronet installIntoSessionConfiguration:config];
session_ = [NSURLSession sessionWithConfiguration:config
delegate:delegate_
delegateQueue:nil];
}
void TearDown() override {
TestServer::Shutdown();
[Cronet stopNetLog];
[Cronet shutdownForTesting];
CronetTestBase::TearDown();
}
NSURLSession* session_;
};
TEST_F(HttpTest, CreateSslKeyLogFile) {
// Shutdown Cronet so that it can be restarted with specific configuration
// (SSL key log file specified in experimental options) for this one test.
// This is necessary because SslKeyLogFile can only be set once, before any
// SSL Client Sockets are created.
[Cronet shutdownForTesting];
NSString* ssl_key_log_file = [Cronet getNetLogPathForFile:@"SSLKEYLOGFILE"];
// Ensure that the keylog file doesn't exist.
[[NSFileManager defaultManager] removeItemAtPath:ssl_key_log_file error:nil];
[Cronet setExperimentalOptions:
[NSString stringWithFormat:@"{\"ssl_key_log_file\":\"%@\"}",
ssl_key_log_file]];
StartCronet(grpc_support::GetQuicTestServerPort());
bool ssl_file_created =
[[NSFileManager defaultManager] fileExistsAtPath:ssl_key_log_file];
[[NSFileManager defaultManager] removeItemAtPath:ssl_key_log_file error:nil];
[Cronet shutdownForTesting];
[Cronet setExperimentalOptions:@""];
EXPECT_TRUE(ssl_file_created);
}
TEST_F(HttpTest, NSURLSessionReceivesData) {
NSURL* url = net::NSURLWithGURL(GURL(grpc_support::kTestServerSimpleUrl));
__block BOOL block_used = NO;
NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
[Cronet setRequestFilterBlock:^(NSURLRequest* request) {
block_used = YES;
EXPECT_EQ([request URL], url);
return YES;
}];
StartDataTaskAndWaitForCompletion(task);
EXPECT_TRUE(block_used);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_STREQ(grpc_support::kSimpleBodyValue,
base::SysNSStringToUTF8([delegate_ responseBody]).c_str());
}
TEST_F(HttpTest, NSURLSessionReceivesBigHttpDataLoop) {
int iterations = 50;
long size = 10 * 1024 * 1024;
LOG(INFO) << "Downloading " << size << " bytes " << iterations << " times.";
NSTimeInterval elapsed_avg = 0;
NSTimeInterval elapsed_max = 0;
NSURL* url = net::NSURLWithGURL(GURL(TestServer::PrepareBigDataURL(size)));
for (int i = 0; i < iterations; ++i) {
[delegate_ reset];
__block BOOL block_used = NO;
NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
[Cronet setRequestFilterBlock:^(NSURLRequest* request) {
block_used = YES;
EXPECT_EQ([request URL], url);
return YES;
}];
NSDate* start = [NSDate date];
StartDataTaskAndWaitForCompletion(task);
NSTimeInterval elapsed = -[start timeIntervalSinceNow];
elapsed_avg += elapsed;
if (elapsed > elapsed_max)
elapsed_max = elapsed;
EXPECT_TRUE(block_used);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_EQ(size, [delegate_ totalBytesReceived]);
}
// Release the response buffer.
TestServer::ReleaseBigDataURL();
LOG(INFO) << "Elapsed Average:" << elapsed_avg * 1000 / iterations
<< "ms Max:" << elapsed_max * 1000 << "ms";
}
TEST_F(HttpTest, GetGlobalMetricsDeltas) {
NSData* delta1 = [Cronet getGlobalMetricsDeltas];
NSURL* url = net::NSURLWithGURL(GURL(grpc_support::kTestServerSimpleUrl));
NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
StartDataTaskAndWaitForCompletion(task);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_STREQ(grpc_support::kSimpleBodyValue,
base::SysNSStringToUTF8([delegate_ responseBody]).c_str());
NSData* delta2 = [Cronet getGlobalMetricsDeltas];
EXPECT_FALSE([delta2 isEqualToData:delta1]);
}
TEST_F(HttpTest, SdchDisabledByDefault) {
NSURL* url =
net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("Accept-Encoding")));
NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
StartDataTaskAndWaitForCompletion(task);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_FALSE([[delegate_ responseBody] containsString:@"sdch"]);
}
// Verify that explictly setting Accept-Encoding request header to 'gzip,sdch"
// is passed to the server and does not trigger any failures. This behavior may
// In the future Cronet may not allow caller to set Accept-Encoding header and
// could limit it to set of internally suported and enabled encodings, matching
// behavior of Cronet on Android.
TEST_F(HttpTest, AcceptEncodingSdchIsAllowed) {
NSURL* url =
net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("Accept-Encoding")));
NSMutableURLRequest* mutableRequest =
[[NSURLRequest requestWithURL:url] mutableCopy];
[mutableRequest addValue:@"gzip,sdch" forHTTPHeaderField:@"Accept-Encoding"];
NSURLSessionDataTask* task = [session_ dataTaskWithRequest:mutableRequest];
StartDataTaskAndWaitForCompletion(task);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_TRUE([[delegate_ responseBody] containsString:@"gzip,sdch"]);
}
// Verify that explictly setting Accept-Encoding request header to 'foo,bar"
// is passed to the server and does not trigger any failures. This behavior may
// In the future Cronet may not allow caller to set Accept-Encoding header and
// could limit it to set of internally suported and enabled encodings, matching
// behavior of Cronet on Android.
TEST_F(HttpTest, AcceptEncodingFooBarIsAllowed) {
NSURL* url =
net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("Accept-Encoding")));
NSMutableURLRequest* mutableRequest =
[[NSURLRequest requestWithURL:url] mutableCopy];
[mutableRequest addValue:@"foo,bar" forHTTPHeaderField:@"Accept-Encoding"];
NSURLSessionDataTask* task = [session_ dataTaskWithRequest:mutableRequest];
StartDataTaskAndWaitForCompletion(task);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_TRUE([[delegate_ responseBody] containsString:@"foo,bar"]);
}
TEST_F(HttpTest, NSURLSessionAcceptLanguage) {
NSURL* url =
net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("Accept-Language")));
NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
StartDataTaskAndWaitForCompletion(task);
EXPECT_EQ(nil, [delegate_ error]);
ASSERT_STREQ("en-US,en", [[delegate_ responseBody] UTF8String]);
}
TEST_F(HttpTest, SetUserAgentIsExact) {
NSURL* url =
net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("User-Agent")));
[Cronet setRequestFilterBlock:nil];
NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
StartDataTaskAndWaitForCompletion(task);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_STREQ(kUserAgent, [[delegate_ responseBody] UTF8String]);
}
TEST_F(HttpTest, SetCookie) {
const char kCookieHeader[] = "Cookie";
NSString* cookieName =
[NSString stringWithFormat:@"SetCookie-%@", [[NSUUID UUID] UUIDString]];
NSString* cookieValue = [[NSUUID UUID] UUIDString];
NSString* cookieLine =
[NSString stringWithFormat:@"%@=%@", cookieName, cookieValue];
NSHTTPCookieStorage* systemCookieStorage =
[NSHTTPCookieStorage sharedHTTPCookieStorage];
NSURL* cookieUrl =
net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL(kCookieHeader)));
// Verify that cookie is not set in system storage.
for (NSHTTPCookie* cookie in [systemCookieStorage cookiesForURL:cookieUrl]) {
EXPECT_FALSE([[cookie name] isEqualToString:cookieName]);
}
StartDataTaskAndWaitForCompletion([session_ dataTaskWithURL:cookieUrl]);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_EQ(nil, [delegate_ responseBody]);
NSURL* setCookieUrl = net::NSURLWithGURL(
GURL(TestServer::GetSetCookieURL(base::SysNSStringToUTF8(cookieLine))));
StartDataTaskAndWaitForCompletion([session_ dataTaskWithURL:setCookieUrl]);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_TRUE([[delegate_ responseBody] containsString:cookieLine]);
StartDataTaskAndWaitForCompletion([session_ dataTaskWithURL:cookieUrl]);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_TRUE([[delegate_ responseBody] containsString:cookieLine]);
// Verify that cookie is set in system storage.
NSHTTPCookie* systemCookie = nil;
for (NSHTTPCookie* cookie in [systemCookieStorage cookiesForURL:cookieUrl]) {
if ([cookie.name isEqualToString:cookieName]) {
systemCookie = cookie;
break;
}
}
EXPECT_TRUE([[systemCookie value] isEqualToString:cookieValue]);
[systemCookieStorage deleteCookie:systemCookie];
}
TEST_F(HttpTest, SetSystemCookie) {
const char kCookieHeader[] = "Cookie";
NSString* cookieName = [NSString
stringWithFormat:@"SetSystemCookie-%@", [[NSUUID UUID] UUIDString]];
NSString* cookieValue = [[NSUUID UUID] UUIDString];
NSHTTPCookieStorage* systemCookieStorage =
[NSHTTPCookieStorage sharedHTTPCookieStorage];
NSURL* echoCookieUrl =
net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL(kCookieHeader)));
NSHTTPCookie* systemCookie = [NSHTTPCookie cookieWithProperties:@{
NSHTTPCookiePath : [echoCookieUrl path],
NSHTTPCookieName : cookieName,
NSHTTPCookieValue : cookieValue,
NSHTTPCookieDomain : [echoCookieUrl host],
}];
[systemCookieStorage setCookie:systemCookie];
StartDataTaskAndWaitForCompletion([session_ dataTaskWithURL:echoCookieUrl]);
[systemCookieStorage deleteCookie:systemCookie];
EXPECT_EQ(nil, [delegate_ error]);
// Verify that cookie set in system store was sent to the serever.
EXPECT_TRUE([[delegate_ responseBody] containsString:cookieName]);
EXPECT_TRUE([[delegate_ responseBody] containsString:cookieValue]);
}
TEST_F(HttpTest, SystemCookieWithNullCreationTime) {
const char kCookieHeader[] = "Cookie";
NSString* cookieName = [NSString
stringWithFormat:@"SetSystemCookie-%@", [[NSUUID UUID] UUIDString]];
NSString* cookieValue = [[NSUUID UUID] UUIDString];
NSHTTPCookieStorage* systemCookieStorage =
[NSHTTPCookieStorage sharedHTTPCookieStorage];
NSURL* echoCookieUrl =
net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL(kCookieHeader)));
NSHTTPCookie* nullCreationTimeCookie = [NSHTTPCookie cookieWithProperties:@{
NSHTTPCookiePath : [echoCookieUrl path],
NSHTTPCookieName : cookieName,
NSHTTPCookieValue : cookieValue,
NSHTTPCookieDomain : [echoCookieUrl host],
@"Created" : [NSNumber numberWithDouble:0.0],
}];
[systemCookieStorage setCookie:nullCreationTimeCookie];
NSHTTPCookie* normalCookie = [NSHTTPCookie cookieWithProperties:@{
NSHTTPCookiePath : [echoCookieUrl path],
NSHTTPCookieName : [cookieName stringByAppendingString:@"-normal"],
NSHTTPCookieValue : cookieValue,
NSHTTPCookieDomain : [echoCookieUrl host],
}];
[systemCookieStorage setCookie:normalCookie];
StartDataTaskAndWaitForCompletion([session_ dataTaskWithURL:echoCookieUrl]);
[systemCookieStorage deleteCookie:nullCreationTimeCookie];
[systemCookieStorage deleteCookie:normalCookie];
EXPECT_EQ(nil, [delegate_ error]);
// Verify that cookie set in system store was sent to the serever.
EXPECT_TRUE([[delegate_ responseBody] containsString:cookieName]);
EXPECT_TRUE([[delegate_ responseBody] containsString:cookieValue]);
}
TEST_F(HttpTest, FilterOutRequest) {
NSURL* url =
net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("User-Agent")));
__block BOOL block_used = NO;
NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
[Cronet setRequestFilterBlock:^(NSURLRequest* request) {
block_used = YES;
EXPECT_EQ([request URL], url);
return NO;
}];
StartDataTaskAndWaitForCompletion(task);
EXPECT_TRUE(block_used);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_FALSE([[delegate_ responseBody]
containsString:base::SysUTF8ToNSString(kUserAgent)]);
EXPECT_TRUE([[delegate_ responseBody] containsString:@"CFNetwork"]);
}
TEST_F(HttpTest, FileSchemeNotSupported) {
NSString* fileData = @"Hello, World!";
NSString* documentsDirectory = [NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* filePath = [documentsDirectory
stringByAppendingPathComponent:[[NSProcessInfo processInfo]
globallyUniqueString]];
[fileData writeToFile:filePath
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
NSURL* url = [NSURL fileURLWithPath:filePath];
NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
[Cronet setRequestFilterBlock:^(NSURLRequest* request) {
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
EXPECT_TRUE(false) << "Block should not be called for unsupported requests";
return YES;
}];
StartDataTaskAndWaitForCompletion(task);
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_TRUE([[delegate_ responseBody] containsString:fileData]);
}
TEST_F(HttpTest, DataSchemeNotSupported) {
NSString* testString = @"Hello, World!";
NSData* testData = [testString dataUsingEncoding:NSUTF8StringEncoding];
NSString* dataString =
[NSString stringWithFormat:@"data:text/plain;base64,%@",
[testData base64EncodedStringWithOptions:0]];
NSURL* url = [NSURL URLWithString:dataString];
NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
[Cronet setRequestFilterBlock:^(NSURLRequest* request) {
EXPECT_TRUE(false) << "Block should not be called for unsupported requests";
return YES;
}];
StartDataTaskAndWaitForCompletion(task);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_TRUE([[delegate_ responseBody] containsString:testString]);
}
} // namespace cronet