| // 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]; |
| config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; |
| [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, 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", |
| base::SysNSStringToUTF8([delegate_ responseBody]).c_str()); |
| } |
| |
| 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, |
| base::SysNSStringToUTF8([delegate_ responseBody]).c_str()); |
| } |
| |
| 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]); |
| } |
| |
| TEST_F(HttpTest, BrotliAdvertisedTest) { |
| [Cronet shutdownForTesting]; |
| |
| [Cronet setBrotliEnabled:YES]; |
| |
| StartCronet(grpc_support::GetQuicTestServerPort()); |
| |
| NSURL* url = |
| net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("Accept-Encoding"))); |
| NSURLSessionDataTask* task = [session_ dataTaskWithURL:url]; |
| StartDataTaskAndWaitForCompletion(task); |
| EXPECT_EQ(nil, [delegate_ error]); |
| EXPECT_TRUE([[delegate_ responseBody] containsString:@"br"]); |
| } |
| |
| TEST_F(HttpTest, BrotliNotAdvertisedTest) { |
| [Cronet shutdownForTesting]; |
| |
| [Cronet setBrotliEnabled:NO]; |
| |
| StartCronet(grpc_support::GetQuicTestServerPort()); |
| |
| 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:@"br"]); |
| } |
| |
| TEST_F(HttpTest, BrotliHandleDecoding) { |
| [Cronet shutdownForTesting]; |
| |
| [Cronet setBrotliEnabled:YES]; |
| |
| StartCronet(grpc_support::GetQuicTestServerPort()); |
| |
| NSURL* url = |
| net::NSURLWithGURL(GURL(TestServer::GetUseEncodingURL("brotli"))); |
| NSURLSessionDataTask* task = [session_ dataTaskWithURL:url]; |
| StartDataTaskAndWaitForCompletion(task); |
| EXPECT_EQ(nil, [delegate_ error]); |
| EXPECT_STREQ(base::SysNSStringToUTF8([delegate_ responseBody]).c_str(), |
| "The quick brown fox jumps over the lazy dog"); |
| } |
| |
| TEST_F(HttpTest, PostRequest) { |
| // Create request body. |
| NSString* request_body = [NSString stringWithFormat:@"Post Data %i", rand()]; |
| NSData* post_data = [request_body dataUsingEncoding:NSUTF8StringEncoding]; |
| |
| // Prepare the request. |
| NSURL* url = net::NSURLWithGURL(GURL(TestServer::EchoRequestBodyURL())); |
| NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url]; |
| request.HTTPMethod = @"POST"; |
| request.HTTPBody = post_data; |
| |
| // Set the request filter to check that the request was handled by the Cronet |
| // stack. |
| __block BOOL block_used = NO; |
| [Cronet setRequestFilterBlock:^(NSURLRequest* req) { |
| block_used = YES; |
| EXPECT_EQ([req URL], url); |
| return YES; |
| }]; |
| |
| // Send the request and wait for the response. |
| NSURLSessionDataTask* data_task = [session_ dataTaskWithRequest:request]; |
| StartDataTaskAndWaitForCompletion(data_task); |
| |
| // Verify that the response from the server matches the request body. |
| NSString* response_body = [delegate_ responseBody]; |
| ASSERT_EQ(nil, [delegate_ error]); |
| ASSERT_STREQ(base::SysNSStringToUTF8(request_body).c_str(), |
| base::SysNSStringToUTF8(response_body).c_str()); |
| ASSERT_TRUE(block_used); |
| } |
| |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| TEST_F(HttpTest, LegacyApi) { |
| NSURL* url = net::NSURLWithGURL(GURL(grpc_support::kTestServerSimpleUrl)); |
| |
| __block BOOL block_used = NO; |
| [Cronet setRequestFilterBlock:^(NSURLRequest* request) { |
| block_used = YES; |
| EXPECT_EQ(request.URL, url); |
| return YES; |
| }]; |
| |
| NSURLRequest* request = [NSURLRequest requestWithURL:url]; |
| NSError* err; |
| NSHTTPURLResponse* response; |
| [NSURLConnection sendSynchronousRequest:request |
| returningResponse:&response |
| error:&err]; |
| |
| EXPECT_EQ(200, [response statusCode]); |
| EXPECT_TRUE(block_used); |
| EXPECT_FALSE(err); |
| } |
| #pragma clang diagnostic pop |
| |
| } // namespace cronet |