Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Fix Infinite Session Replay Processing Loop (#5765)
- Fix memory leak in SessionReplayIntegration (#5770)
- Fix reporting of energy used while profiling (#5768)
- Add null-handling for parsed DSN in SentryHTTPTransport (#5800)

### Internal

Expand Down
19 changes: 11 additions & 8 deletions Sources/Sentry/SentryHttpTransport.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ @interface SentryHttpTransport ()
@property (nonatomic, strong) SentryFileManager *fileManager;
@property (nonatomic, strong) id<SentryRequestManager> requestManager;
@property (nonatomic, strong) SentryNSURLRequestBuilder *requestBuilder;
@property (nonatomic, strong) SentryOptions *options;
@property (nonatomic, strong) SentryDsn *dsn;
@property (nonatomic) BOOL sendClientReports;
@property (nonatomic, strong) id<SentryRateLimits> rateLimits;
@property (nonatomic, strong) SentryEnvelopeRateLimit *envelopeRateLimit;
@property (nonatomic, strong) SentryDispatchQueueWrapper *dispatchQueue;
Expand Down Expand Up @@ -67,7 +68,8 @@ @interface SentryHttpTransport ()

@implementation SentryHttpTransport

- (id)initWithOptions:(SentryOptions *)options
- (id)initWithDsn:(SentryDsn *)dsn
sendClientReports:(BOOL)sendClientReports
cachedEnvelopeSendDelay:(NSTimeInterval)cachedEnvelopeSendDelay
dateProvider:(id<SentryCurrentDateProvider>)dateProvider
fileManager:(SentryFileManager *)fileManager
Expand All @@ -78,7 +80,8 @@ - (id)initWithOptions:(SentryOptions *)options
dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper
{
if (self = [super init]) {
self.options = options;
self.dsn = dsn;
self.sendClientReports = sendClientReports;
_cachedEnvelopeSendDelay = cachedEnvelopeSendDelay;
self.requestManager = requestManager;
self.requestBuilder = requestBuilder;
Expand Down Expand Up @@ -162,7 +165,7 @@ - (void)recordLostEvent:(SentryDataCategory)category
reason:(SentryDiscardReason)reason
quantity:(NSUInteger)quantity
{
if (!self.options.sendClientReports) {
if (!self.sendClientReports) {
return;
}

Expand Down Expand Up @@ -276,7 +279,7 @@ - (void)envelopeItemDeleted:(SentryEnvelopeItem *)envelopeItem

- (SentryEnvelope *)addClientReportTo:(SentryEnvelope *)envelope
{
if (!self.options.sendClientReports) {
if (!self.sendClientReports) {
return envelope;
}

Expand Down Expand Up @@ -357,14 +360,14 @@ - (void)sendAllCachedEnvelopes
// We must set sentAt as close as possible to the transmission of the envelope to Sentry.
rateLimitedEnvelope.header.sentAt = [self.dateProvider date];

NSError *requestError = nil;
NSError *_Nullable requestError = nil;
NSURLRequest *request = [self.requestBuilder createEnvelopeRequest:rateLimitedEnvelope
dsn:self.options.parsedDsn
dsn:self.dsn
didFailWithError:&requestError];

if (nil == request || nil != requestError) {
if (nil != requestError) {
SENTRY_LOG_DEBUG(@"Failed to build request: %@.", requestError);
SENTRY_LOG_FATAL(@"Failed to build request to send envelope: %@.", requestError);
}
[self recordLostEventFor:rateLimitedEnvelope.items];
[self deleteEnvelopeAndSendNext:envelopeFilePath];
Expand Down
28 changes: 19 additions & 9 deletions Sources/Sentry/SentryTransportFactory.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#import "SentryEnvelopeRateLimit.h"
#import "SentryHttpDateParser.h"
#import "SentryHttpTransport.h"
#import "SentryInternalDefines.h"
#import "SentryLogC.h"
#import "SentryNSURLRequestBuilder.h"
#import "SentryOptions.h"
#import "SentryQueueableRequestManager.h"
Expand All @@ -27,6 +29,13 @@ @implementation SentryTransportFactory
sentryFileManager:(SentryFileManager *)sentryFileManager
rateLimits:(id<SentryRateLimits>)rateLimits
{
if (!options.parsedDsn) {
SENTRY_LOG_FATAL(@"Failed to create transports because the SentryOptions does not contain "
@"a parsed DSN.");
return @[];
}
SentryDsn *_Nonnull dsn = SENTRY_UNWRAP_NULLABLE(SentryDsn, options.parsedDsn);

NSURLSession *session;

if (options.urlSession) {
Expand Down Expand Up @@ -54,15 +63,16 @@ @implementation SentryTransportFactory
SentryNSURLRequestBuilder *requestBuilder = [[SentryNSURLRequestBuilder alloc] init];

SentryHttpTransport *httpTransport =
[[SentryHttpTransport alloc] initWithOptions:options
cachedEnvelopeSendDelay:0.1
dateProvider:dateProvider
fileManager:sentryFileManager
requestManager:requestManager
requestBuilder:requestBuilder
rateLimits:rateLimits
envelopeRateLimit:envelopeRateLimit
dispatchQueueWrapper:dispatchQueueWrapper];
[[SentryHttpTransport alloc] initWithDsn:dsn
sendClientReports:options.sendClientReports
cachedEnvelopeSendDelay:0.1
dateProvider:dateProvider
fileManager:sentryFileManager
requestManager:requestManager
requestBuilder:requestBuilder
rateLimits:rateLimits
envelopeRateLimit:envelopeRateLimit
dispatchQueueWrapper:dispatchQueueWrapper];

if (options.enableSpotlight) {
SentrySpotlightTransport *spotlightTransport =
Expand Down
5 changes: 3 additions & 2 deletions Sources/Sentry/include/SentryHttpTransport.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

@class SentryDispatchQueueWrapper;
@class SentryNSURLRequestBuilder;
@class SentryOptions;
@class SentryDsn;
@protocol SentryCurrentDateProvider;

NS_ASSUME_NONNULL_BEGIN
Expand All @@ -16,7 +16,8 @@ NS_ASSUME_NONNULL_BEGIN
: NSObject <SentryTransport, SentryEnvelopeRateLimitDelegate, SentryFileManagerDelegate>
SENTRY_NO_INIT

- (id)initWithOptions:(SentryOptions *)options
- (id)initWithDsn:(SentryDsn *)dsn
sendClientReports:(BOOL)sendClientReports
cachedEnvelopeSendDelay:(NSTimeInterval)cachedEnvelopeSendDelay
dateProvider:(id<SentryCurrentDateProvider>)dateProvider
fileManager:(SentryFileManager *)fileManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ final class SentryHttpTransportFlushIntegrationTests: XCTestCase {
let dispatchQueueWrapper = SentryDispatchQueueWrapper()

return (SentryHttpTransport(
options: options,
dsn: try XCTUnwrap(options.parsedDsn),
sendClientReports: options.sendClientReports,
cachedEnvelopeSendDelay: 0.0,
dateProvider: SentryDefaultCurrentDateProvider(),
fileManager: fileManager,
Expand Down
45 changes: 24 additions & 21 deletions Tests/SentryTests/Networking/SentryHttpTransportTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,10 @@ class SentryHttpTransportTests: XCTestCase {
func getSut(
fileManager: SentryFileManager? = nil,
dispatchQueueWrapper: SentryDispatchQueueWrapper? = nil
) -> SentryHttpTransport {
) throws -> SentryHttpTransport {
return SentryHttpTransport(
options: options,
dsn: try XCTUnwrap(options.parsedDsn),
sendClientReports: options.sendClientReports,
cachedEnvelopeSendDelay: 0.0,
dateProvider: currentDateProvider,
fileManager: fileManager ?? self.fileManager,
Expand All @@ -163,13 +164,13 @@ class SentryHttpTransportTests: XCTestCase {
private var fixture: Fixture!
private var sut: SentryHttpTransport!

override func setUp() {
override func setUpWithError() throws {
super.setUp()
fixture = Fixture()
fixture.fileManager.deleteAllEnvelopes()
fixture.requestManager.returnResponse(response: HTTPURLResponse())

sut = fixture.getSut()
sut = try fixture.getSut()
}

override func tearDown() {
Expand All @@ -179,14 +180,14 @@ class SentryHttpTransportTests: XCTestCase {
clearTestState()
}

func testInitSendsCachedEnvelopes() {
func testInitSendsCachedEnvelopes() throws {
givenNoInternetConnection()
sendEventAsync()
assertEnvelopesStored(envelopeCount: 1)

waitForAllRequests()
givenOkResponse()
let sut = fixture.getSut()
let sut = try fixture.getSut()
XCTAssertNotNil(sut)
waitForAllRequests()

Expand Down Expand Up @@ -520,8 +521,8 @@ class SentryHttpTransportTests: XCTestCase {
func testFailureToStoreEvenlopeEventStillSendsRequest() throws {
let fileManger = try TestFileManager(options: fixture.options)
fileManger.storeEnvelopePathNil = true // Failure to store envelope returns nil path
let sut = fixture.getSut(fileManager: fileManger)
let sut = try fixture.getSut(fileManager: fileManger)

sut.send(envelope: fixture.eventEnvelope)

XCTAssertEqual(fileManger.storeEnvelopeInvocations.count, 1)
Expand Down Expand Up @@ -750,13 +751,13 @@ class SentryHttpTransportTests: XCTestCase {
fixture.dispatchQueueWrapper.dispatchAfterExecutesBlock = false

// Interact with sut in extra function so ARC deallocates it
func getSut() {
let sut = fixture.getSut()
func getSut() throws {
let sut = try fixture.getSut()
sut.send(envelope: fixture.eventEnvelope)
waitForAllRequests()
}
getSut()
try getSut()

for dispatchAfterBlock in fixture.dispatchQueueWrapper.dispatchAfterInvocations.invocations {
dispatchAfterBlock.block()
}
Expand Down Expand Up @@ -841,21 +842,23 @@ class SentryHttpTransportTests: XCTestCase {
assertClientReportStoredInMemory()
}

func testSendClientReportsDisabled_DoesNotRecordLostEvents() {
func testSendClientReportsDisabled_DoesNotRecordLostEvents() throws {
fixture.options.sendClientReports = false
sut = try fixture.getSut()
givenErrorResponse()

sendEvent()

assertClientReportNotStoredInMemory()
}

func testSendClientReportsDisabled_DoesSendClientReport() {
func testSendClientReportsDisabled_DoesSendClientReport() throws {
givenErrorResponse()
sendEvent()

givenOkResponse()
fixture.options.sendClientReports = false
sut = try fixture.getSut()
sendEvent()

assertEventIsSentAsEnvelope()
Expand Down Expand Up @@ -917,18 +920,18 @@ class SentryHttpTransportTests: XCTestCase {
XCTAssertEqual(2, fixture.requestManager.requests.count)
}

func testDealloc_StopsReachabilityMonitoring() {
func deallocSut() {
_ = fixture.getSut()
func testDealloc_StopsReachabilityMonitoring() throws {
func deallocSut() throws {
_ = try fixture.getSut()
}
deallocSut()
try deallocSut()

XCTAssertEqual(1, fixture.reachability.stopMonitoringInvocations.count)
}

func testDealloc_TriggerNetworkReachable_NoCrash() {
_ = fixture.getSut()
func testDealloc_TriggerNetworkReachable_NoCrash() throws {
_ = try fixture.getSut()

fixture.reachability.triggerNetworkReachable()
}
#endif // !os(watchOS)
Expand Down
42 changes: 37 additions & 5 deletions Tests/SentryTests/Networking/SentryTransportFactoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import Sentry
import XCTest

class SentryTransportFactoryTests: XCTestCase {

private static let dsnAsString = TestConstants.dsnAsString(username: "SentryTransportFactoryTests")

func testIntegration_UrlSessionDelegate_PassedToRequestManager() throws {
// -- Arrange --
let urlSessionDelegateSpy = UrlSessionDelegateSpy()

let expect = expectation(description: "UrlSession Delegate of Options called in RequestManager")
Expand All @@ -19,6 +19,8 @@ class SentryTransportFactoryTests: XCTestCase {
options.urlSessionDelegate = urlSessionDelegateSpy

let fileManager = try! SentryFileManager(options: options, dispatchQueueWrapper: TestSentryDispatchQueueWrapper())

// -- Act --
let transports = TransportInitializer.initTransports(
options,
dateProvider: SentryDependencyContainer.sharedInstance().dateProvider,
Expand All @@ -32,11 +34,13 @@ class SentryTransportFactoryTests: XCTestCase {
let request = URLRequest(url: imgUrl)

requestManager.add(request) { _, _ in /* We don't care about the result */ }

// -- Assert --
wait(for: [expect], timeout: 10)
}

func testShouldReturnTransports_WhenURLSessionPassed() throws {

// -- Arrange --
let urlSessionDelegateSpy = UrlSessionDelegateSpy()
let expect = expectation(description: "UrlSession Delegate of Options called in RequestManager")

Expand All @@ -46,9 +50,12 @@ class SentryTransportFactoryTests: XCTestCase {
}

let options = Options()
options.dsn = SentryTransportFactoryTests.dsnAsString
options.urlSession = sessionConfiguration

let fileManager = try! SentryFileManager(options: options, dispatchQueueWrapper: TestSentryDispatchQueueWrapper())

// -- Act --
let transports = TransportInitializer.initTransports(
options,
dateProvider: SentryDependencyContainer.sharedInstance().dateProvider,
Expand All @@ -63,35 +70,60 @@ class SentryTransportFactoryTests: XCTestCase {
let request = URLRequest(url: imgUrl)

requestManager.add(request) { _, _ in /* We don't care about the result */ }

// -- Assert --
wait(for: [expect], timeout: 10)

}

func testShouldReturnTwoTransports_WhenSpotlightEnabled() throws {
// -- Arrange --
let options = Options()
options.dsn = SentryTransportFactoryTests.dsnAsString
options.enableSpotlight = true

// -- Act --
let transports = TransportInitializer.initTransports(
options,
dateProvider: SentryDependencyContainer.sharedInstance().dateProvider,
sentryFileManager: try SentryFileManager(options: options),
rateLimits: rateLimiting()
)


// -- Assert --
XCTAssertEqual(transports.count, 2)
XCTAssert(transports.contains {
$0.isKind(of: SentrySpotlightTransport.self)
})

XCTAssert(transports.contains {
$0.isKind(of: SentryHttpTransport.self)
})
}

func testInitTransports_whenOptionsParsedDsnNil_shouldReturnEmptyTransports() throws {
// -- Arrange --
let options = Options()
options.dsn = nil

// -- Act --
let transports = TransportInitializer.initTransports(
options,
dateProvider: SentryDependencyContainer.sharedInstance().dateProvider,
sentryFileManager: try SentryFileManager(options: options),
rateLimits: rateLimiting()
)

// -- Assert --
XCTAssertEqual(transports.count, 0)
}

// MARK: - Helpers

private func rateLimiting() -> RateLimits {
let dateProvider = TestCurrentDateProvider()
let retryAfterHeaderParser = RetryAfterHeaderParser(httpDateParser: HttpDateParser(), currentDateProvider: dateProvider)
let rateLimitParser = RateLimitParser(currentDateProvider: dateProvider)

return DefaultRateLimits(retryAfterHeaderParser: retryAfterHeaderParser, andRateLimitParser: rateLimitParser, currentDateProvider: dateProvider)
}

}
Loading