Skip to content

Commit

Permalink
Merge 994721f into 58cec58
Browse files Browse the repository at this point in the history
  • Loading branch information
pmarkowsky committed Oct 1, 2021
2 parents 58cec58 + 994721f commit b86e606
Show file tree
Hide file tree
Showing 8 changed files with 359 additions and 4 deletions.
3 changes: 2 additions & 1 deletion Source/santametricservice/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ objc_library(
"//Source/common:SNTXPCMetricServiceInterface",
"//Source/santametricservice/Formats:SNTMetricRawJSONFormat",
"//Source/santametricservice/Writers:SNTMetricFileWriter",
"//Source/santametricservice/Writers:SNTMetricHTTPWriter",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
Expand All @@ -39,7 +40,7 @@ test_suite(
tests = [
":SNTMetricServiceTest",
"//Source/santametricservice/Formats:SNTMetricRawJSONFormatTest",
"//Source/santametricservice/Writers:SNTMetricFileWriterTest",
"//Source/santametricservice/Writers:writer_tests",
],
)

Expand Down
2 changes: 1 addition & 1 deletion Source/santametricservice/Formats/SNTMetricRawJSONFormat.m
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ - (NSDictionary *)normalize:(NSDictionary *)metrics {
if (![NSJSONSerialization isValidJSONObject:normalizedMetrics]) {
if (err != nil) {
*err = [[NSError alloc]
initWithDomain:@"SNTMetricRawJSONFileWriter"
initWithDomain:@"com.google.santa.metricservice.formatters.rawjson"
code:EINVAL
userInfo:@{
NSLocalizedDescriptionKey : @"unable to convert metrics to JSON: invalid metrics"
Expand Down
5 changes: 4 additions & 1 deletion Source/santametricservice/SNTMetricService.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#import "SNTMetricService.h"
#import "Source/santametricservice/Formats/SNTMetricRawJSONFormat.h"
#import "Source/santametricservice/Writers/SNTMetricFileWriter.h"
#import "Source/santametricservice/Writers/SNTMetricHTTPWriter.h"

@interface SNTMetricService ()
@property MOLXPCConnection *notifierConnection;
Expand All @@ -38,7 +39,9 @@ - (instancetype)init {
self = [super init];
if (self) {
rawJSONFormatter = [[SNTMetricRawJSONFormat alloc] init];
metricWriters = @{@"file" : [[SNTMetricFileWriter alloc] init]};
metricWriters = @{@"file" : [[SNTMetricFileWriter alloc] init],
@"http": [[SNTMetricHTTPWriter alloc] init],
};

_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
}
Expand Down
61 changes: 61 additions & 0 deletions Source/santametricservice/SNTMetricServiceTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#import "Source/common/SNTMetricSet.h"

#import <OCMock/OCMock.h>
#import <MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.h>

#import "Source/santametricservice/Formats/SNTMetricFormatTestHelper.h"
#import "Source/santametricservice/SNTMetricService.h"
Expand All @@ -16,6 +17,9 @@ @interface SNTMetricServiceTest : XCTestCase
@property id mockConfigurator;
@property NSString *tempDir;
@property NSURL *jsonURL;
@property id mockSession;
@property id mockSessionDataTask;
@property id mockMOLAuthenticatingURLSession;
@end

@implementation SNTMetricServiceTest
Expand All @@ -39,6 +43,15 @@ - (void)setUp {

- (void)tearDown {
[self.mockConfigurator stopMocking];
if (self.mockSessionDataTask != nil) {
[self.mockSessionDataTask stopMocking];
}
if (self.mockSession != nil) {
[self.mockSession stopMocking];
}
if (self.mockMOLAuthenticatingURLSession != nil) {
[self.mockMOLAuthenticatingURLSession stopMocking];
}

// delete the temp dir
[[NSFileManager defaultManager] removeItemAtPath:self.tempDir error:NULL];
Expand Down Expand Up @@ -112,4 +125,52 @@ - (void)testWritingRawJSONFile {

XCTAssertEqualObjects(validMetricsDict, parsedJSONData, @"invalid JSON created");
}

- (void)testWritingJSON {
NSURL *url = [NSURL URLWithString:@"http://localhost:9444"];
OCMStub([self.mockConfigurator exportMetrics]).andReturn(YES);
OCMStub([self.mockConfigurator metricFormat]).andReturn(SNTMetricFormatTypeRawJSON);
OCMStub([self.mockConfigurator metricURL]).andReturn(url);

self.mockSession = [OCMockObject niceMockForClass:[NSURLSession class]];
self.mockSessionDataTask = [OCMockObject niceMockForClass:[NSURLSessionDataTask class]];
self.mockMOLAuthenticatingURLSession =
[OCMockObject niceMockForClass:[MOLAuthenticatingURLSession class]];

[[[self.mockMOLAuthenticatingURLSession stub] andReturn:self.mockMOLAuthenticatingURLSession] alloc];
[[[self.mockMOLAuthenticatingURLSession stub] andReturn:self.mockSession] session];

NSHTTPURLResponse *response =
[[NSHTTPURLResponse alloc] initWithURL:url
statusCode:200
HTTPVersion:@"HTTP/1.1"
headerFields:@{@"content-type" : @"application/json"}];

__block void (^passedBlock)(NSData *, NSURLResponse *, NSError *);

XCTestExpectation *responseCallback = [[XCTestExpectation alloc] initWithDescription:@"ensure writer passed JSON"];

void (^getCompletionHandler)(NSInvocation *) = ^(NSInvocation *invocation) {
[invocation getArgument:&passedBlock atIndex:3];
};

void (^callCompletionHandler)(NSInvocation *) = ^(NSInvocation *invocation) {
passedBlock(nil, response, nil);
[responseCallback fulfill];
};


// stub out session to call completion handler immediately.
[(NSURLSessionDataTask *)[[self.mockSessionDataTask stub] andDo:callCompletionHandler] resume];

// stub out NSURLSession to assign our completion handler and return our mock
[[[[self.mockSession stub] andDo:getCompletionHandler] andReturn:self.mockSessionDataTask]
dataTaskWithRequest:[OCMArg any]
completionHandler:[OCMArg any]];

SNTMetricService *service = [[SNTMetricService alloc] init];
[service exportForMonitoring:[SNTMetricFormatTestHelper createValidMetricsDictionary]];
[self waitForExpectations:@[responseCallback] timeout:10.0];
}
@end

33 changes: 32 additions & 1 deletion Source/santametricservice/Writers/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ objc_library(
srcs = [
"SNTMetricFileWriter.h",
"SNTMetricFileWriter.m",
"SNTMetricWriter.h",
],
deps = [
":SNTMetricWriter",
Expand All @@ -31,3 +30,35 @@ santa_unit_test(
":SNTMetricFileWriter",
],
)

objc_library(
name = "SNTMetricHTTPWriter",
srcs = [
"SNTMetricHTTPWriter.h",
"SNTMetricHTTPWriter.m",
],
deps = [
":SNTMetricWriter",
"//Source/common:SNTLogging",
"@MOLAuthenticatingURLSession",
],
)

santa_unit_test(
name = "SNTMetricHTTPWriterTest",
srcs = [
"SNTMetricHTTPWriterTest.m",
],
deps = [
":SNTMetricHTTPWriter",
"@OCMock",
],
)

test_suite(
name = "writer_tests",
tests = [
":SNTMetricFileWriterTest",
":SNTMetricHTTPWriterTest",
],
)
18 changes: 18 additions & 0 deletions Source/santametricservice/Writers/SNTMetricHTTPWriter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// Copyright 2021 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.

#import "Source/santametricservice/Writers/SNTMetricWriter.h"

@interface SNTMetricHTTPWriter : NSObject <SNTMetricWriter>
@end
97 changes: 97 additions & 0 deletions Source/santametricservice/Writers/SNTMetricHTTPWriter.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/// Copyright 2021 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include <dispatch/dispatch.h>
#import <MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.h>

#import "Source/santametricservice/Writers/SNTMetricHTTPWriter.h"

@implementation SNTMetricHTTPWriter {
@private
NSMutableURLRequest *_request;
MOLAuthenticatingURLSession *_authSession;
NSURLSession *_session;
}

- (instancetype)init {
self = [super init];
if (self) {
_request = [[NSMutableURLRequest alloc] init];
_request.HTTPMethod = @"POST";
_authSession = [[MOLAuthenticatingURLSession alloc] init];
}
return self;
}

/**
* Post serialzied metrics to the specified URL one object at a time.
**/
- (BOOL)write:(NSArray<NSData *> *)metrics toURL:(NSURL *)url error:(NSError **)error {
// open the file and write it.
__block NSError *_blockError = nil;

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

_authSession.serverHostname = url.host;
_session = _authSession.session;

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[metrics enumerateObjectsUsingBlock:^(id value, NSUInteger index, BOOL *stop) {
request.HTTPBody = (NSData *)value;
[[_session dataTaskWithRequest:request
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response,
NSError *_Nullable err) {
if (err != nil) {
_blockError = err;
*stop = YES;
}

if (response == nil) {
*stop = YES;
} else if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;

// Check HTTP error codes.
if (httpResponse && httpResponse.statusCode != 200) {
_blockError = [[NSError alloc]
initWithDomain:@"com.google.santa.metricservice.writers.http"
code:httpResponse.statusCode
userInfo:@{
NSLocalizedDescriptionKey :
[NSString stringWithFormat:@"received http status code %ld from %@",
httpResponse.statusCode, url]
}];

*stop = YES;
}
}
}] resume];
}];
dispatch_semaphore_signal(semaphore);
});

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

if (_blockError != nil) {
if (error != nil) {
*error = [_blockError copy];
}
return NO;
}

return YES;
}
@end
Loading

0 comments on commit b86e606

Please sign in to comment.