Skip to content
Merged
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Fixes

- Add null-handling for parsed DSN in SentryHTTPTransport (#5800)

## 8.54.1-alpha.0

> [!Important]
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
82 changes: 54 additions & 28 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,34 +29,37 @@ @implementation SentryTransportFactory
sentryFileManager:(SentryFileManager *)sentryFileManager
rateLimits:(id<SentryRateLimits>)rateLimits
{
NSURLSession *session;

if (options.urlSession) {
session = options.urlSession;
} else {
NSURLSessionConfiguration *configuration =
[NSURLSessionConfiguration ephemeralSessionConfiguration];
session = [NSURLSession sessionWithConfiguration:configuration
delegate:options.urlSessionDelegate
delegateQueue:nil];
}
NSMutableArray<id<SentryTransport>> *transports = [NSMutableArray array];

NSURLSession *session = [self getUrlSession:options];
id<SentryRequestManager> requestManager =
[[SentryQueueableRequestManager alloc] initWithSession:session];

SentryEnvelopeRateLimit *envelopeRateLimit =
[[SentryEnvelopeRateLimit alloc] initWithRateLimits:rateLimits];

dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(
DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_PRIORITY_LOW, 0);
SentryDispatchQueueWrapper *dispatchQueueWrapper =
[[SentryDispatchQueueWrapper alloc] initWithName:"io.sentry.http-transport"
attributes:attributes];
SentryDispatchQueueWrapper *dispatchQueueWrapper = [self createDispatchQueueWrapper];

SentryNSURLRequestBuilder *requestBuilder = [[SentryNSURLRequestBuilder alloc] init];

SentryHttpTransport *httpTransport =
[[SentryHttpTransport alloc] initWithOptions:options
if (options.enableSpotlight) {
SENTRY_LOG_DEBUG(@"Spotlight is enabled, creating Spotlight transport.");
SentrySpotlightTransport *spotlightTransport =
[[SentrySpotlightTransport alloc] initWithOptions:options
requestManager:requestManager
requestBuilder:requestBuilder
dispatchQueueWrapper:dispatchQueueWrapper];

[transports addObject:spotlightTransport];
} else {
SENTRY_LOG_DEBUG(@"Spotlight is disabled in options, not adding Spotlight transport.");
}

if (options.parsedDsn) {
SENTRY_LOG_DEBUG(@"Options contain parsed DSN, creating HTTP transport.");
SentryDsn *_Nonnull dsn = SENTRY_UNWRAP_NULLABLE(SentryDsn, options.parsedDsn);

SentryHttpTransport *httpTransport =
[[SentryHttpTransport alloc] initWithDsn:dsn
sendClientReports:options.sendClientReports
cachedEnvelopeSendDelay:0.1
dateProvider:dateProvider
fileManager:sentryFileManager
Expand All @@ -64,18 +69,39 @@ @implementation SentryTransportFactory
envelopeRateLimit:envelopeRateLimit
dispatchQueueWrapper:dispatchQueueWrapper];

if (options.enableSpotlight) {
SentrySpotlightTransport *spotlightTransport =
[[SentrySpotlightTransport alloc] initWithOptions:options
requestManager:requestManager
requestBuilder:requestBuilder
dispatchQueueWrapper:dispatchQueueWrapper];
return @[ httpTransport, spotlightTransport ];
[transports addObject:httpTransport];
} else {
return @[ httpTransport ];
SENTRY_LOG_WARN(
@"Failed to create HTTP transport because the SentryOptions does not contain "
@"a parsed DSN.");
}

return transports;
}

+ (NSURLSession *)getUrlSession:(SentryOptions *_Nonnull)options
{
if (options.urlSession) {
SENTRY_LOG_DEBUG(@"Using URL session provided in SDK options for HTTP transport.");
return SENTRY_UNWRAP_NULLABLE(NSURLSession, options.urlSession);
}

NSURLSessionConfiguration *configuration =
[NSURLSessionConfiguration ephemeralSessionConfiguration];
return [NSURLSession sessionWithConfiguration:configuration
delegate:options.urlSessionDelegate
delegateQueue:nil];
}

+ (SentryDispatchQueueWrapper *)createDispatchQueueWrapper
{
dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(
DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_PRIORITY_LOW, 0);
SentryDispatchQueueWrapper *dispatchQueueWrapper =
[[SentryDispatchQueueWrapper alloc] initWithName:"io.sentry.http-transport"
attributes:attributes];
return dispatchQueueWrapper;
}
@end

NS_ASSUME_NONNULL_END
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
Loading
Loading