@@ -26,21 +26,26 @@ import struct TSCUtility.Version
26
26
public final class RegistryClient : Cancellable {
27
27
private static let apiVersion : APIVersion = . v1
28
28
private static let availabilityCacheTTL : DispatchTimeInterval = . seconds( 5 * 60 )
29
+ private static let metadataCacheTTL : DispatchTimeInterval = . seconds( 60 * 60 )
29
30
30
31
private let configuration : RegistryConfiguration
31
32
private let archiverProvider : ( FileSystem ) -> Archiver
32
33
private let httpClient : LegacyHTTPClient
33
34
private let authorizationProvider : LegacyHTTPClientConfiguration . AuthorizationProvider ?
34
- private let signingEntityStorage : PackageSigningEntityStorage ?
35
- private let signingEntityCheckingMode : SigningEntityCheckingMode
36
35
private let jsonDecoder : JSONDecoder
37
36
38
37
private var checksumTOFU : PackageVersionChecksumTOFU !
38
+ private let signingEntityTOFU : PackageSigningEntityTOFU
39
39
40
40
private let availabilityCache = ThreadSafeKeyValueStore <
41
41
URL ,
42
42
( status: Result < AvailabilityStatus , Error > , expires: DispatchTime )
43
43
> ( )
44
+
45
+ private let metadataCache = ThreadSafeKeyValueStore <
46
+ MetadataCacheKey ,
47
+ ( metadata: Serialization . VersionMetadata , expires: DispatchTime )
48
+ > ( )
44
49
45
50
public init (
46
51
configuration: RegistryConfiguration ,
@@ -80,10 +85,12 @@ public final class RegistryClient: Cancellable {
80
85
81
86
self . httpClient = customHTTPClient ?? LegacyHTTPClient ( )
82
87
self . archiverProvider = customArchiverProvider ?? { fileSystem in ZipArchiver ( fileSystem: fileSystem) }
83
- self . signingEntityStorage = signingEntityStorage
84
- self . signingEntityCheckingMode = signingEntityCheckingMode
85
88
self . jsonDecoder = JSONDecoder . makeWithDefaults ( )
86
89
90
+ self . signingEntityTOFU = PackageSigningEntityTOFU (
91
+ signingEntityStorage: signingEntityStorage,
92
+ signingEntityCheckingMode: signingEntityCheckingMode
93
+ )
87
94
self . checksumTOFU = PackageVersionChecksumTOFU (
88
95
fingerprintStorage: fingerprintStorage,
89
96
fingerprintCheckingMode: fingerprintCheckingMode,
@@ -321,7 +328,6 @@ public final class RegistryClient: Cancellable {
321
328
}
322
329
}
323
330
324
- // TODO: add caching
325
331
// marked internal for testing
326
332
func _getRawPackageVersionMetadata(
327
333
registry: Registry ,
@@ -333,6 +339,11 @@ public final class RegistryClient: Cancellable {
333
339
completion: @escaping ( Result < Serialization . VersionMetadata , Error > ) -> Void
334
340
) {
335
341
let completion = self . makeAsync ( completion, on: callbackQueue)
342
+
343
+ let cacheKey = MetadataCacheKey ( registry: registry, package : package )
344
+ if let cached = self . metadataCache [ cacheKey] , cached. expires < . now( ) {
345
+ return completion ( . success( cached. metadata) )
346
+ }
336
347
337
348
guard var components = URLComponents ( url: registry. url, resolvingAgainstBaseURL: true ) else {
338
349
return completion ( . failure( RegistryError . invalidURL ( registry. url) ) )
@@ -357,10 +368,12 @@ public final class RegistryClient: Cancellable {
357
368
result. tryMap { response in
358
369
switch response. statusCode {
359
370
case 200 :
360
- return try response. parseJSON (
371
+ let metadata = try response. parseJSON (
361
372
Serialization . VersionMetadata. self,
362
373
decoder: self . jsonDecoder
363
374
)
375
+ self . metadataCache [ cacheKey] = ( metadata: metadata, expires: . now( ) + Self. metadataCacheTTL)
376
+ return metadata
364
377
case 404 :
365
378
throw RegistryError . packageVersionNotFound
366
379
default :
@@ -1182,6 +1195,11 @@ public final class RegistryClient: Cancellable {
1182
1195
options. authorizationProvider = self . authorizationProvider
1183
1196
return options
1184
1197
}
1198
+
1199
+ private struct MetadataCacheKey : Hashable {
1200
+ let registry : Registry
1201
+ let package : PackageIdentity . RegistryIdentity
1202
+ }
1185
1203
}
1186
1204
1187
1205
public enum RegistryError : Error , CustomStringConvertible {
@@ -1216,6 +1234,18 @@ public enum RegistryError: Error, CustomStringConvertible {
1216
1234
case registryNotAvailable( Registry )
1217
1235
case packageNotFound
1218
1236
case packageVersionNotFound
1237
+ case sourceArchiveNotSigned( registry: Registry , package : PackageIdentity , version: Version )
1238
+ case failedLoadingSignature
1239
+ case failedRetrievingSourceArchiveSignature( registry: Registry , package : PackageIdentity , version: Version , error: Error )
1240
+ case missingConfiguration( details: String )
1241
+ case missingSignatureFormat
1242
+ case unknownSignatureFormat( String )
1243
+ case invalidSignature( reason: String )
1244
+ case invalidSigningCertificate( reason: String )
1245
+ case signerNotTrusted
1246
+ case failedToValidateSignature( Error )
1247
+ case signingEntityForReleaseHasChanged( package : PackageIdentity , version: Version , latest: SigningEntity ? , previous: SigningEntity )
1248
+ case signingEntityForPackageHasChanged( package : PackageIdentity , latest: SigningEntity ? , previous: SigningEntity )
1219
1249
1220
1250
public var description : String {
1221
1251
switch self {
@@ -1280,11 +1310,35 @@ public enum RegistryError: Error, CustomStringConvertible {
1280
1310
case . forbidden:
1281
1311
return " Forbidden "
1282
1312
case . registryNotAvailable( let registry) :
1283
- return " registry at '\( registry. url) ' is not available at this time, please try again later "
1313
+ return " Registry at '\( registry. url) ' is not available at this time, please try again later "
1284
1314
case . packageNotFound:
1285
- return " package not found on registry"
1315
+ return " Package not found on registry"
1286
1316
case . packageVersionNotFound:
1287
- return " package version not found on registry "
1317
+ return " Package version not found on registry "
1318
+ case . sourceArchiveNotSigned( let registry, let packageIdentity, let version) :
1319
+ return " ' \( packageIdentity) @ \( version) ' source archive from ' \( registry) ' is not signed "
1320
+ case . failedLoadingSignature:
1321
+ return " Failed loading signature for validation "
1322
+ case . failedRetrievingSourceArchiveSignature( let registry, let packageIdentity, let version, let error) :
1323
+ return " Failed retrieving ' \( packageIdentity) @ \( version) ' source archive signature from ' \( registry) ': \( error) "
1324
+ case . missingConfiguration( let details) :
1325
+ return " Unable to proceed because of missing configuration: \( details) "
1326
+ case . missingSignatureFormat:
1327
+ return " Missing signature format "
1328
+ case . unknownSignatureFormat( let format) :
1329
+ return " Unknown signature format: \( format) "
1330
+ case . invalidSignature( let reason) :
1331
+ return " Signature is invalid: \( reason) "
1332
+ case . invalidSigningCertificate( let reason) :
1333
+ return " The signing certificate is invalid: \( reason) "
1334
+ case . signerNotTrusted:
1335
+ return " The signer is not trusted "
1336
+ case . failedToValidateSignature( let error) :
1337
+ return " Failed to validate signature: \( error) "
1338
+ case . signingEntityForReleaseHasChanged( let package , let version, let latest, let previous) :
1339
+ return " The signing entity ' \( String ( describing: latest) ) ' for ' \( package ) @ \( version) ' is different from the previously recorded value ' \( previous) ' "
1340
+ case . signingEntityForPackageHasChanged( let package , let latest, let previous) :
1341
+ return " The signing entity ' \( String ( describing: latest) ) ' for ' \( package ) ' is different from the previously recorded value ' \( previous) ' "
1288
1342
}
1289
1343
}
1290
1344
}
@@ -1613,6 +1667,10 @@ extension RegistryClient {
1613
1667
public let version : String
1614
1668
public let resources : [ Resource ]
1615
1669
public let metadata : AdditionalMetadata ?
1670
+
1671
+ var sourceArchive : Resource ? {
1672
+ self . resources. first ( where: { $0. name == " source-archive " } )
1673
+ }
1616
1674
1617
1675
public init (
1618
1676
id: String ,
0 commit comments