Skip to content

Commit 6b8cbcb

Browse files
itaybrephilprime
authored andcommitted
ref: Convert SentryRateLimitParser to Swift (#6283)
* ref: Convert `SentryRateLimitParser` to Swift * Fix case where parameters were just numbers
1 parent ff55f6a commit 6b8cbcb

File tree

9 files changed

+137
-180
lines changed

9 files changed

+137
-180
lines changed

Sentry.xcodeproj/project.pbxproj

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -551,8 +551,6 @@
551551
7BE0DC2F272ABAF6004FA8B7 /* SentryAutoBreadcrumbTrackingIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BE0DC2E272ABAF6004FA8B7 /* SentryAutoBreadcrumbTrackingIntegrationTests.swift */; };
552552
7BE2C7F8257000A4003B66C7 /* SentryTestIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BE2C7F72570009F003B66C7 /* SentryTestIntegration.m */; };
553553
7BE3C7752445C82300A38442 /* SentryCurrentDateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BE3C7742445C82300A38442 /* SentryCurrentDateTests.swift */; };
554-
7BE3C77B2446111500A38442 /* SentryRateLimitParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BE3C77A2446111500A38442 /* SentryRateLimitParser.h */; };
555-
7BE3C77D2446112C00A38442 /* SentryRateLimitParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BE3C77C2446112C00A38442 /* SentryRateLimitParser.m */; };
556554
7BE3C78724472E9800A38442 /* TestRequestManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BE3C78624472E9800A38442 /* TestRequestManager.swift */; };
557555
7BE8E8462593313500C4DA1F /* SentryAttachment+Equality.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BE8E8452593313500C4DA1F /* SentryAttachment+Equality.m */; };
558556
7BE912AB272162AF00E49E62 /* SentryNoOpSpan.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BE912AA272162AF00E49E62 /* SentryNoOpSpan.h */; };
@@ -992,6 +990,7 @@
992990
F41362112E1C55AF00B84443 /* SentryScopePersistentStore+Tags.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41362102E1C55AF00B84443 /* SentryScopePersistentStore+Tags.swift */; };
993991
F41362132E1C566100B84443 /* SentryScopePersistentStore+User.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41362122E1C566100B84443 /* SentryScopePersistentStore+User.swift */; };
994992
F41362152E1C568400B84443 /* SentryScopePersistentStore+Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41362142E1C568400B84443 /* SentryScopePersistentStore+Context.swift */; };
993+
F429D3AA2E8562EF00DBF387 /* RateLimitParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F429D3A82E8562EF00DBF387 /* RateLimitParser.swift */; };
995994
F429D37F2E8532A300DBF387 /* HttpDateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F429D37D2E8532A300DBF387 /* HttpDateParser.swift */; };
996995
F443DB272E09BE8C009A9045 /* LoadValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F443DB262E09BE8C009A9045 /* LoadValidatorTests.swift */; };
997996
F44858132E03579D0013E63B /* SentryCrashDynamicLinker+Test.h in Headers */ = {isa = PBXBuildFile; fileRef = F44858122E0357940013E63B /* SentryCrashDynamicLinker+Test.h */; };
@@ -1831,8 +1830,6 @@
18311830
7BE2C7F72570009F003B66C7 /* SentryTestIntegration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTestIntegration.m; sourceTree = "<group>"; };
18321831
7BE3C7742445C82300A38442 /* SentryCurrentDateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryCurrentDateTests.swift; sourceTree = "<group>"; };
18331832
7BE3C7762445E50A00A38442 /* TestCurrentDateProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestCurrentDateProvider.swift; sourceTree = "<group>"; };
1834-
7BE3C77A2446111500A38442 /* SentryRateLimitParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryRateLimitParser.h; path = include/SentryRateLimitParser.h; sourceTree = "<group>"; };
1835-
7BE3C77C2446112C00A38442 /* SentryRateLimitParser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryRateLimitParser.m; sourceTree = "<group>"; };
18361833
7BE3C77E2446116400A38442 /* SentryRateLimitsParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryRateLimitsParserTests.swift; sourceTree = "<group>"; };
18371834
7BE3C78624472E9800A38442 /* TestRequestManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestRequestManager.swift; sourceTree = "<group>"; };
18381835
7BE8E8442593313500C4DA1F /* SentryAttachment+Equality.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryAttachment+Equality.h"; sourceTree = "<group>"; };
@@ -2337,6 +2334,7 @@
23372334
F41362102E1C55AF00B84443 /* SentryScopePersistentStore+Tags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+Tags.swift"; sourceTree = "<group>"; };
23382335
F41362122E1C566100B84443 /* SentryScopePersistentStore+User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+User.swift"; sourceTree = "<group>"; };
23392336
F41362142E1C568400B84443 /* SentryScopePersistentStore+Context.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+Context.swift"; sourceTree = "<group>"; };
2337+
F429D3A82E8562EF00DBF387 /* RateLimitParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RateLimitParser.swift; sourceTree = "<group>"; };
23402338
F429D37D2E8532A300DBF387 /* HttpDateParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpDateParser.swift; sourceTree = "<group>"; };
23412339
F443DB262E09BE8C009A9045 /* LoadValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadValidatorTests.swift; sourceTree = "<group>"; };
23422340
F44858122E0357940013E63B /* SentryCrashDynamicLinker+Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryCrashDynamicLinker+Test.h"; sourceTree = "<group>"; };
@@ -3504,8 +3502,6 @@
35043502
7BBD18922449BEDD00427C76 /* SentryDefaultRateLimits.m */,
35053503
7B56D73024616CCD00B842DA /* SentryConcurrentRateLimitsDictionary.h */,
35063504
7B56D73224616D9500B842DA /* SentryConcurrentRateLimitsDictionary.m */,
3507-
7BE3C77A2446111500A38442 /* SentryRateLimitParser.h */,
3508-
7BE3C77C2446112C00A38442 /* SentryRateLimitParser.m */,
35093505
7BBD189C244EC71A00427C76 /* SentryRetryAfterHeaderParser.h */,
35103506
7BBD189D244EC8D200427C76 /* SentryRetryAfterHeaderParser.m */,
35113507
7B3398622459C14000BD9C96 /* SentryEnvelopeRateLimit.h */,
@@ -4726,6 +4722,7 @@
47264722
isa = PBXGroup;
47274723
children = (
47284724
F429D37D2E8532A300DBF387 /* HttpDateParser.swift */,
4725+
F429D3A82E8562EF00DBF387 /* RateLimitParser.swift */,
47294726
);
47304727
path = Networking;
47314728
sourceTree = "<group>";
@@ -4991,7 +4988,6 @@
49914988
62862B1C2B1DDBC8009B16E3 /* SentryDelayedFrame.h in Headers */,
49924989
627E7589299F6FE40085504D /* SentryInternalDefines.h in Headers */,
49934990
FA3854362E267DA60045A563 /* SentryUser+Serialize.h in Headers */,
4994-
7BE3C77B2446111500A38442 /* SentryRateLimitParser.h in Headers */,
49954991
7D0637032382B34300B30749 /* SentryScope.h in Headers */,
49964992
03F84D2727DD414C008FE43F /* SentryMachLogging.hpp in Headers */,
49974993
63295AF51EF3C7DB002D4490 /* SentryNSDictionarySanitize.h in Headers */,
@@ -5624,7 +5620,6 @@
56245620
FAF1201A2E70C0EE006E1DA3 /* SentryEnvelopeHeaderHelper.m in Sources */,
56255621
F49D419E2DEA3D0600D9244E /* SentryCrashExceptionApplicationHelper.m in Sources */,
56265622
D8ACE3C82762187200F5A213 /* SentryFileIOTracker.m in Sources */,
5627-
7BE3C77D2446112C00A38442 /* SentryRateLimitParser.m in Sources */,
56285623
D8B088B729C9E3FF00213258 /* SentryTracerConfiguration.m in Sources */,
56295624
FA7206E12E0B37C80072FDD4 /* SentryProfileCollector.mm in Sources */,
56305625
9264E1EB2E2E385E00B077CF /* SentryLogMessage.swift in Sources */,
@@ -5960,6 +5955,7 @@
59605955
FACEED132E3179A10007B4AC /* SentyOptionsInternal.m in Sources */,
59615956
8482FA9C2DD7C397000E9283 /* SentryFeedbackAPI.m in Sources */,
59625957
7BC9A20628F41781001E7C4C /* SentryMeasurementUnit.m in Sources */,
5958+
F429D3AA2E8562EF00DBF387 /* RateLimitParser.swift in Sources */,
59635959
63FE71A020DA4C1100CDBAE8 /* SentryCrashInstallation.m in Sources */,
59645960
63FE713520DA4C1100CDBAE8 /* SentryCrashMemory.c in Sources */,
59655961
629194A92D51F976000F7C6B /* SentryDebugMetaCodable.swift in Sources */,

Sources/Sentry/SentryDefaultRateLimits.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#import "SentryDateUtil.h"
55
#import "SentryInternalDefines.h"
66
#import "SentryLogC.h"
7-
#import "SentryRateLimitParser.h"
87
#import "SentryRetryAfterHeaderParser.h"
98
#import "SentrySwift.h"
109

Sources/Sentry/SentryDependencyContainer.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#import <SentryDefaultRateLimits.h>
2121
#import <SentryDependencyContainer.h>
2222
#import <SentryPerformanceTracker.h>
23-
#import <SentryRateLimitParser.h>
2423
#import <SentryRetryAfterHeaderParser.h>
2524
#import <SentrySDK+Private.h>
2625
#import <SentrySwift.h>

Sources/Sentry/SentryRateLimitParser.m

Lines changed: 0 additions & 113 deletions
This file was deleted.

Sources/Sentry/SentryTransportFactory.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#import "SentryNSURLRequestBuilder.h"
88
#import "SentryOptions.h"
99
#import "SentryQueueableRequestManager.h"
10-
#import "SentryRateLimitParser.h"
1110
#import "SentryRateLimits.h"
1211
#import "SentrySwift.h"
1312

Sources/Sentry/include/SentryRateLimitParser.h

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
@_implementationOnly import _SentryPrivate
2+
import Foundation
3+
4+
/** Parses the custom X-Sentry-Rate-Limits header.
5+
6+
This header exists of a multiple quotaLimits seperated by ",".
7+
Each quotaLimit exists of retry_after:categories:scope.
8+
retry_after: seconds until the rate limit expires.
9+
categories: semicolon separated list of categories. If empty, this limit
10+
applies to all categories. scope: This can be ignored by SDKs.
11+
*/
12+
@objc(SentryRateLimitParser) @_spi(Private)
13+
public final class RateLimitParser: NSObject {
14+
15+
private let currentDateProvider: SentryCurrentDateProvider
16+
17+
@objc
18+
public init(currentDateProvider: SentryCurrentDateProvider) {
19+
self.currentDateProvider = currentDateProvider
20+
super.init()
21+
}
22+
23+
@objc
24+
public func parse(_ header: String) -> [UInt: Date] {
25+
guard !header.isEmpty else {
26+
return [:]
27+
}
28+
29+
var rateLimits: [UInt: Date] = [:]
30+
31+
// The header might contain whitespaces and they must be ignored.
32+
let headerNoWhitespaces = removeAllWhitespaces(header)
33+
34+
// Each quotaLimit exists of retryAfter:categories:scope. The scope is
35+
// ignored here as it can be ignored by SDKs.
36+
for quota in headerNoWhitespaces.components(separatedBy: ",") {
37+
let parameters = quota.components(separatedBy: ":")
38+
39+
guard parameters.count >= 2,
40+
let rateLimitInSeconds = parseRateLimitSeconds(parameters[0]),
41+
rateLimitInSeconds.intValue > 0 else {
42+
continue
43+
}
44+
45+
for categoryNumber in parseCategories(parameters[1]) {
46+
let dataCategory = sentryDataCategoryForNSUInteger(categoryNumber)
47+
48+
// Namespaces should only be available for MetricBucket
49+
if dataCategory == .metricBucket && parameters.count > 4 {
50+
let namespacesAsString = parameters[4]
51+
let namespaces = namespacesAsString.components(separatedBy: ";")
52+
53+
if namespacesAsString.isEmpty || namespaces.contains("custom") {
54+
rateLimits[categoryNumber] = getLongerRateLimit(
55+
existingRateLimit: rateLimits[categoryNumber],
56+
rateLimitInSeconds: rateLimitInSeconds
57+
)
58+
}
59+
} else {
60+
rateLimits[categoryNumber] = getLongerRateLimit(
61+
existingRateLimit: rateLimits[categoryNumber],
62+
rateLimitInSeconds: rateLimitInSeconds
63+
)
64+
}
65+
}
66+
}
67+
68+
return rateLimits
69+
}
70+
71+
private func removeAllWhitespaces(_ string: String) -> String {
72+
let words = string.components(separatedBy: .whitespacesAndNewlines)
73+
return words.joined()
74+
}
75+
76+
private func parseRateLimitSeconds(_ string: String) -> NSNumber? {
77+
let numberFormatter = NumberFormatter()
78+
numberFormatter.numberStyle = .none
79+
return numberFormatter.number(from: string)
80+
}
81+
82+
private func parseCategories(_ categoriesAsString: String) -> [UInt] {
83+
// The categories are a semicolon separated list. If this parameter is empty
84+
// it stands for all categories. componentsSeparatedByString returns one
85+
// category even if this parameter is empty.
86+
var categories: [UInt] = []
87+
88+
for categoryAsString in categoriesAsString.components(separatedBy: ";") {
89+
let category = sentryDataCategoryForString(categoryAsString)
90+
91+
// Unknown categories must be ignored. UserFeedback is not listed for rate limits, see
92+
// https://develop.sentry.dev/sdk/rate-limiting/#definitions
93+
if category != .unknown && category != .userFeedback {
94+
categories.append(category.rawValue)
95+
}
96+
}
97+
98+
return categories
99+
}
100+
101+
private func getLongerRateLimit(
102+
existingRateLimit: Date?,
103+
rateLimitInSeconds: NSNumber
104+
) -> Date {
105+
let newDate = currentDateProvider.date().addingTimeInterval(rateLimitInSeconds.doubleValue)
106+
return SentryDateUtil.getMaximumDate(newDate, andOther: existingRateLimit) ?? newDate
107+
}
108+
}

0 commit comments

Comments
 (0)