| Index: components/cronet/ios/test/cronet_test_base.mm
|
| diff --git a/components/cronet/ios/test/cronet_test_base.mm b/components/cronet/ios/test/cronet_test_base.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a056ab0bee5805a5ae28d767b9f37a2e92f539e0
|
| --- /dev/null
|
| +++ b/components/cronet/ios/test/cronet_test_base.mm
|
| @@ -0,0 +1,188 @@
|
| +// Copyright 2017 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 "cronet_test_base.h"
|
| +
|
| +#include "components/grpc_support/test/quic_test_server.h"
|
| +#include "crypto/sha2.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/cert/asn1_util.h"
|
| +#include "net/cert/mock_cert_verifier.h"
|
| +#include "net/test/cert_test_util.h"
|
| +#include "net/test/test_data_directory.h"
|
| +
|
| +#pragma mark
|
| +
|
| +@implementation TestDelegate {
|
| + // Completion semaphore for this TestDelegate. When the request this delegate
|
| + // is attached to finishes (either successfully or with an error), this
|
| + // delegate signals this semaphore.
|
| + dispatch_semaphore_t _semaphore;
|
| +}
|
| +
|
| +@synthesize error = _error;
|
| +@synthesize totalBytesReceived = _totalBytesReceived;
|
| +
|
| +NSMutableArray<NSData*>* _responseData;
|
| +
|
| +- (id)init {
|
| + if (self = [super init]) {
|
| + _semaphore = dispatch_semaphore_create(0);
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)reset {
|
| + _responseData = nil;
|
| + _error = nil;
|
| + _totalBytesReceived = 0;
|
| +}
|
| +
|
| +- (NSString*)responseBody {
|
| + if (_responseData == nil) {
|
| + return nil;
|
| + }
|
| + NSMutableString* body = [NSMutableString string];
|
| + for (NSData* data in _responseData) {
|
| + [body appendString:[[NSString alloc] initWithData:data
|
| + encoding:NSUTF8StringEncoding]];
|
| + }
|
| + VLOG(3) << "responseBody size:" << [body length]
|
| + << " chunks:" << [_responseData count];
|
| + return body;
|
| +}
|
| +
|
| +- (BOOL)waitForDone {
|
| + int64_t deadline_ns = 20 * NSEC_PER_SEC;
|
| + return dispatch_semaphore_wait(
|
| + _semaphore, dispatch_time(DISPATCH_TIME_NOW, deadline_ns)) == 0;
|
| +}
|
| +
|
| +- (void)URLSession:(NSURLSession*)session
|
| + didBecomeInvalidWithError:(NSError*)error {
|
| +}
|
| +
|
| +- (void)URLSession:(NSURLSession*)session
|
| + task:(NSURLSessionTask*)task
|
| + didCompleteWithError:(NSError*)error {
|
| + [self setError:error];
|
| + dispatch_semaphore_signal(_semaphore);
|
| +}
|
| +
|
| +- (void)URLSession:(NSURLSession*)session
|
| + task:(NSURLSessionTask*)task
|
| + didReceiveChallenge:(NSURLAuthenticationChallenge*)challenge
|
| + completionHandler:
|
| + (void (^)(NSURLSessionAuthChallengeDisposition disp,
|
| + NSURLCredential* credential))completionHandler {
|
| + completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
|
| +}
|
| +
|
| +- (void)URLSession:(NSURLSession*)session
|
| + dataTask:(NSURLSessionDataTask*)dataTask
|
| + didReceiveResponse:(NSURLResponse*)response
|
| + completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))
|
| + completionHandler {
|
| + completionHandler(NSURLSessionResponseAllow);
|
| +}
|
| +
|
| +- (void)URLSession:(NSURLSession*)session
|
| + dataTask:(NSURLSessionDataTask*)dataTask
|
| + didReceiveData:(NSData*)data {
|
| + _totalBytesReceived += [data length];
|
| + if (_responseData == nil) {
|
| + _responseData = [[NSMutableArray alloc] init];
|
| + }
|
| + [_responseData addObject:data];
|
| +}
|
| +
|
| +- (void)URLSession:(NSURLSession*)session
|
| + dataTask:(NSURLSessionDataTask*)dataTask
|
| + willCacheResponse:(NSCachedURLResponse*)proposedResponse
|
| + completionHandler:
|
| + (void (^)(NSCachedURLResponse* cachedResponse))completionHandler {
|
| + completionHandler(proposedResponse);
|
| +}
|
| +
|
| +@end
|
| +
|
| +namespace cronet {
|
| +
|
| +void CronetTestBase::SetUp() {
|
| + ::testing::Test::SetUp();
|
| + grpc_support::StartQuicTestServer();
|
| + delegate_ = [[TestDelegate alloc] init];
|
| +}
|
| +
|
| +void CronetTestBase::TearDown() {
|
| + grpc_support::ShutdownQuicTestServer();
|
| + ::testing::Test::TearDown();
|
| +}
|
| +
|
| +// Launches the supplied |task| and blocks until it completes, with a timeout
|
| +// of 1 second.
|
| +void CronetTestBase::StartDataTaskAndWaitForCompletion(
|
| + NSURLSessionDataTask* task) {
|
| + [delegate_ reset];
|
| + [task resume];
|
| + CHECK([delegate_ waitForDone]);
|
| +}
|
| +
|
| +::testing::AssertionResult CronetTestBase::IsResponseSuccessful() {
|
| + if ([delegate_ error])
|
| + return ::testing::AssertionFailure() << "error in response: " <<
|
| + [[[delegate_ error] description]
|
| + cStringUsingEncoding:NSUTF8StringEncoding];
|
| + else
|
| + return ::testing::AssertionSuccess() << "no errors in response";
|
| +}
|
| +
|
| +std::unique_ptr<net::MockCertVerifier> CronetTestBase::CreateMockCertVerifier(
|
| + const std::vector<std::string>& certs,
|
| + bool known_root) {
|
| + std::unique_ptr<net::MockCertVerifier> mock_cert_verifier(
|
| + new net::MockCertVerifier());
|
| + for (const auto& cert : certs) {
|
| + net::CertVerifyResult verify_result;
|
| + verify_result.verified_cert =
|
| + net::ImportCertFromFile(net::GetTestCertsDirectory(), cert);
|
| +
|
| + // By default, HPKP verification is enabled for known trust roots only.
|
| + verify_result.is_issued_by_known_root = known_root;
|
| +
|
| + // Calculate the public key hash and add it to the verify_result.
|
| + net::HashValue hashValue;
|
| + CHECK(CalculatePublicKeySha256(*verify_result.verified_cert.get(),
|
| + &hashValue));
|
| + verify_result.public_key_hashes.push_back(hashValue);
|
| +
|
| + mock_cert_verifier->AddResultForCert(verify_result.verified_cert.get(),
|
| + verify_result, net::OK);
|
| + }
|
| + return mock_cert_verifier;
|
| +}
|
| +
|
| +bool CronetTestBase::CalculatePublicKeySha256(const net::X509Certificate& cert,
|
| + net::HashValue* out_hash_value) {
|
| + // Convert the cert to DER encoded bytes.
|
| + std::string der_cert_bytes;
|
| + net::X509Certificate::OSCertHandle cert_handle = cert.os_cert_handle();
|
| + if (!net::X509Certificate::GetDEREncoded(cert_handle, &der_cert_bytes)) {
|
| + LOG(INFO) << "Unable to convert the given cert to DER encoding";
|
| + return false;
|
| + }
|
| + // Extract the public key from the cert.
|
| + base::StringPiece spki_bytes;
|
| + if (!net::asn1::ExtractSPKIFromDERCert(der_cert_bytes, &spki_bytes)) {
|
| + LOG(INFO) << "Unable to retrieve the public key from the DER cert";
|
| + return false;
|
| + }
|
| + // Calculate SHA256 hash of public key bytes.
|
| + out_hash_value->tag = net::HASH_VALUE_SHA256;
|
| + crypto::SHA256HashString(spki_bytes, out_hash_value->data(),
|
| + crypto::kSHA256Length);
|
| + return true;
|
| +}
|
| +
|
| +} // namespace cronet
|
|
|