blob: a056ab0bee5805a5ae28d767b9f37a2e92f539e0 [file] [log] [blame]
// 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