blob: 91148e194e475105fc3e8079a13a7b187d3c5f0f [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 "TargetConditionals.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "components/cronet/cronet_buildflags.h"
#include "components/cronet/ios/test/cronet_test_base.h"
#include "components/cronet/ios/test/start_cronet.h"
#include "components/cronet/test/test_server.h"
#include "net/base/mac/url_conversions.h"
#include "net/base/net_errors.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/test/quic_simple_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "url/gurl.h"
namespace {
// The buffer size of the stream for HTTPBodyStream post test.
const NSUInteger kRequestBodyBufferLength = 1024;
// The buffer size of the stream for HTTPBodyStream post test when
// testing the stream buffered data size larger than the net stack internal
// buffer size.
const NSUInteger kLargeRequestBodyBufferLength = 100 * kRequestBodyBufferLength;
// The body data write times for HTTPBodyStream post test.
const NSInteger kRequestBodyWriteTimes = 16;
}
@interface StreamBodyRequestDelegate : NSObject<NSStreamDelegate>
- (void)setOutputStream:(NSOutputStream*)outputStream;
- (NSMutableString*)requestBody;
@end
@implementation StreamBodyRequestDelegate {
NSOutputStream* _stream;
NSInteger _count;
NSMutableString* _requestBody;
}
- (instancetype)init {
_requestBody = [NSMutableString string];
return self;
}
- (void)setOutputStream:(NSOutputStream*)outputStream {
_stream = outputStream;
}
- (NSMutableString*)requestBody {
return _requestBody;
}
- (void)stream:(NSStream*)stream handleEvent:(NSStreamEvent)event {
ASSERT_EQ(stream, _stream);
switch (event) {
case NSStreamEventHasSpaceAvailable: {
if (_count < kRequestBodyWriteTimes) {
uint8_t buffer[kRequestBodyBufferLength];
memset(buffer, 'a' + _count, kRequestBodyBufferLength);
NSUInteger bytes_write =
[_stream write:buffer maxLength:kRequestBodyBufferLength];
ASSERT_EQ(kRequestBodyBufferLength, bytes_write);
[_requestBody appendString:[[NSString alloc]
initWithBytes:buffer
length:kRequestBodyBufferLength
encoding:NSUTF8StringEncoding]];
++_count;
} else {
[_stream close];
}
break;
}
case NSStreamEventErrorOccurred:
case NSStreamEventEndEncountered: {
[_stream close];
[_stream setDelegate:nil];
[_stream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
break;
}
default:
break;
}
}
@end
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(net::QuicSimpleTestServer::GetPort());
[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(net::QuicSimpleTestServer::GetPort());
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(net::QuicSimpleTestServer::GetSimpleURL());
__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_EQ(net::QuicSimpleTestServer::GetSimpleBodyValue(),
base::SysNSStringToUTF8([delegate_ responseBody]));
}
// https://crbug.com/830005 Disable histogram support to reduce binary size.
#if BUILDFLAG(DISABLE_HISTOGRAM_SUPPORT)
#define MAYBE_GetGlobalMetricsDeltas DISABLED_GetGlobalMetricsDeltas
#else // BUILDFLAG(DISABLE_HISTOGRAM_SUPPORT)
#define MAYBE_GetGlobalMetricsDeltas GetGlobalMetricsDeltas
#endif // BUILDFLAG(DISABLE_HISTOGRAM_SUPPORT)
TEST_F(HttpTest, MAYBE_GetGlobalMetricsDeltas) {
NSData* delta1 = [Cronet getGlobalMetricsDeltas];
NSURL* url = net::NSURLWithGURL(net::QuicSimpleTestServer::GetSimpleURL());
NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
StartDataTaskAndWaitForCompletion(task);
EXPECT_EQ(nil, [delegate_ error]);
EXPECT_EQ(net::QuicSimpleTestServer::GetSimpleBodyValue(),
base::SysNSStringToUTF8([delegate_ responseBody]));
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_STREQ("Header not found. :(",
base::SysNSStringToUTF8([delegate_ responseBody]).c_str());
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(net::QuicSimpleTestServer::GetPort());
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(net::QuicSimpleTestServer::GetPort());
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(net::QuicSimpleTestServer::GetPort());
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::GetEchoRequestBodyURL()));
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);
}
TEST_F(HttpTest, PostRequestWithLargeBody) {
// Create request body.
std::string request_body(kLargeRequestBodyBufferLength, 'z');
NSData* post_data = [NSData dataWithBytes:request_body.c_str()
length:request_body.length()];
// Prepare the request.
NSURL* url = net::NSURLWithGURL(GURL(TestServer::GetEchoRequestBodyURL()));
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(request_body.c_str(),
base::SysNSStringToUTF8(response_body).c_str());
ASSERT_TRUE(block_used);
}
// Verify the chunked request body upload function.
TEST_F(HttpTest, PostRequestWithBodyStream) {
// Create request body stream.
CFReadStreamRef read_stream = NULL;
CFWriteStreamRef write_stream = NULL;
CFStreamCreateBoundPair(NULL, &read_stream, &write_stream,
kRequestBodyBufferLength);
NSInputStream* input_stream = CFBridgingRelease(read_stream);
NSOutputStream* output_stream = CFBridgingRelease(write_stream);
StreamBodyRequestDelegate* stream_delegate =
[[StreamBodyRequestDelegate alloc] init];
output_stream.delegate = stream_delegate;
[stream_delegate setOutputStream:output_stream];
dispatch_queue_t queue =
dispatch_queue_create("data upload queue", DISPATCH_QUEUE_SERIAL);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
[output_stream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[output_stream open];
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
dispatch_semaphore_signal(semaphore);
});
// Prepare the request.
NSURL* url = net::NSURLWithGURL(GURL(TestServer::GetEchoRequestBodyURL()));
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBodyStream = input_stream;
// 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.
ASSERT_EQ(nil, [delegate_ error]);
NSString* response_body = [delegate_ responseBody];
NSMutableString* request_body = [stream_delegate requestBody];
ASSERT_STREQ(base::SysNSStringToUTF8(request_body).c_str(),
base::SysNSStringToUTF8(response_body).c_str());
ASSERT_TRUE(block_used);
// Wait for the run loop of the child thread exits. Timeout is 5 seconds.
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
ASSERT_EQ(0, dispatch_semaphore_wait(semaphore, timeout));
}
// Verify that the chunked data uploader can correctly handle the request body
// if the stream contains data length exceed the internal upload buffer.
TEST_F(HttpTest, PostRequestWithLargeBodyStream) {
// Create request body stream.
CFReadStreamRef read_stream = NULL;
CFWriteStreamRef write_stream = NULL;
// 100KB data is written in one time.
CFStreamCreateBoundPair(NULL, &read_stream, &write_stream,
kLargeRequestBodyBufferLength);
NSInputStream* input_stream = CFBridgingRelease(read_stream);
NSOutputStream* output_stream = CFBridgingRelease(write_stream);
[output_stream open];
uint8_t buffer[kLargeRequestBodyBufferLength];
memset(buffer, 'a', kLargeRequestBodyBufferLength);
NSUInteger bytes_write =
[output_stream write:buffer maxLength:kLargeRequestBodyBufferLength];
ASSERT_EQ(kLargeRequestBodyBufferLength, bytes_write);
[output_stream close];
// Prepare the request.
NSURL* url = net::NSURLWithGURL(GURL(TestServer::GetEchoRequestBodyURL()));
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBodyStream = input_stream;
// 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.
ASSERT_EQ(nil, [delegate_ error]);
NSString* response_body = [delegate_ responseBody];
ASSERT_EQ(kLargeRequestBodyBufferLength, [response_body length]);
ASSERT_TRUE(block_used);
}
// iOS Simulator doesn't support changing thread priorities.
// Therefore, run these tests only on a physical device.
#if TARGET_OS_SIMULATOR
#define MAYBE_ChangeThreadPriorityAfterStart \
DISABLED_ChangeThreadPriorityAfterStart
#define MAYBE_ChangeThreadPriorityBeforeStart \
DISABLED_ChangeThreadPriorityBeforeStart
#else
#define MAYBE_ChangeThreadPriorityAfterStart ChangeThreadPriorityAfterStart
#define MAYBE_ChangeThreadPriorityBeforeStart ChangeThreadPriorityBeforeStart
#endif // TARGET_OS_SIMULATOR
// Tests that the network thread priority can be changed after
// Cronet has been started.
TEST_F(HttpTest, MAYBE_ChangeThreadPriorityAfterStart) {
// Get current (default) priority of the network thread.
__block double default_priority;
PostBlockToNetworkThread(FROM_HERE, ^{
default_priority = NSThread.threadPriority;
});
// Modify the network thread priority.
const double new_priority = 1.0;
[Cronet setNetworkThreadPriority:new_priority];
// Get modified priority of the network thread.
dispatch_semaphore_t lock = dispatch_semaphore_create(0);
__block double actual_priority;
PostBlockToNetworkThread(FROM_HERE, ^{
actual_priority = NSThread.threadPriority;
dispatch_semaphore_signal(lock);
});
// Wait until the posted tasks are completed.
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
EXPECT_EQ(0.5, default_priority);
// Check that the priority was modified and is close to the set priority.
EXPECT_TRUE(abs(actual_priority - new_priority) < 0.01)
<< "Unexpected thread priority. Expected " << new_priority << " but got "
<< actual_priority;
}
// Tests that the network thread priority can be changed before
// Cronet has been started.
TEST_F(HttpTest, MAYBE_ChangeThreadPriorityBeforeStart) {
// Start a new Cronet engine modifying the network thread priority before the
// start.
[Cronet shutdownForTesting];
const double new_priority = 0.8;
[Cronet setNetworkThreadPriority:new_priority];
[Cronet start];
// Get modified priority of the network thread.
dispatch_semaphore_t lock = dispatch_semaphore_create(0);
__block double actual_priority;
PostBlockToNetworkThread(FROM_HERE, ^{
actual_priority = NSThread.threadPriority;
dispatch_semaphore_signal(lock);
});
// Wait until the posted task is completed.
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
// Check that the priority was modified and is close to the set priority.
EXPECT_TRUE(abs(actual_priority - new_priority) < 0.01)
<< "Unexpected thread priority. Expected " << new_priority << " but got "
<< actual_priority;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
TEST_F(HttpTest, LegacyApi) {
NSURL* url = net::NSURLWithGURL(net::QuicSimpleTestServer::GetSimpleURL());
__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