Skip to content

Commit 82a9c6c

Browse files
authored
Merge pull request #13 from lluuaapp/master
Added Support for Custom Hosts/Regions
2 parents 1faabf2 + 5bc36d9 commit 82a9c6c

File tree

6 files changed

+206
-61
lines changed

6 files changed

+206
-61
lines changed

Package.resolved

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/S3/Extensions/S3+Bucket.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public extension S3 {
7474

7575
let content = """
7676
<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
77-
<LocationConstraint>\(region.rawValue)</LocationConstraint>
77+
<LocationConstraint>\(region.name.rawValue)</LocationConstraint>
7878
</CreateBucketConfiguration>
7979
"""
8080

Sources/S3/Extensions/S3+List.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ extension S3 {
1414
/// Get list of objects
1515
public func list(bucket: String, region: Region? = nil, headers: [String: String], on container: Container) throws -> Future<BucketResults> {
1616
let region = region ?? signer.config.region
17-
guard let baseUrl = URL(string: "https://\(bucket).s3.\(region.rawValue).amazonaws.com/"), let host = baseUrl.host,
17+
guard let baseUrl = URL(string: region.hostUrlString(bucket: bucket)), let host = baseUrl.host,
1818
var components = URLComponents(string: baseUrl.absoluteString) else {
1919
throw S3.Error.invalidUrl
2020
}

Sources/S3/URLBuilder/URLBuilder.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,22 @@ extension Region {
1414

1515
/// Host URL including scheme
1616
public func hostUrlString(bucket: String? = nil) -> String {
17+
if hostName != nil {
18+
return urlProtocol + host.finished(with: "/") + (bucket ?? "")
19+
}
20+
1721
var bucket = bucket
18-
if let b = bucket {
22+
if let b = bucket, !b.isEmpty {
1923
bucket = b + "."
2024
}
21-
return "https://" + (bucket ?? "") + host.finished(with: "/")
25+
26+
return urlProtocol + (bucket ?? "") + host.finished(with: "/")
2227
}
2328

29+
private var urlProtocol: String {
30+
return useTLS ? "https://" : "http://"
31+
}
32+
2433
}
2534

2635

Sources/S3Signer/Region.swift

Lines changed: 186 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,64 +2,200 @@ import Foundation
22

33

44
/// AWS Region
5-
public enum Region: String, Codable {
6-
7-
/// US East (N. Virginia)
8-
case usEast1 = "us-east-1"
9-
10-
/// US East (Ohio)
11-
case usEast2 = "us-east-2"
12-
13-
/// US West (N. California)
14-
case usWest1 = "us-west-1"
15-
16-
/// US West (Oregon)
17-
case usWest2 = "us-west-2"
18-
19-
/// Canada (Central)
20-
case caCentral1 = "ca-central-1"
21-
22-
/// EU (Frankfurt)
23-
case euCentral1 = "eu-central-1"
24-
25-
/// EU (Ireland)
26-
case euWest1 = "eu-west-1"
27-
28-
/// EU (London)
29-
case euWest2 = "eu-west-2"
30-
31-
/// EU (Paris)
32-
case euWest3 = "eu-west-3"
33-
34-
/// Asia Pacific (Tokyo)
35-
case apNortheast1 = "ap-northeast-1"
36-
37-
/// Asia Pacific (Seoul)
38-
case apNortheast2 = "ap-northeast-2"
39-
40-
/// Asia Pacific (Osaka-Local)
41-
case apNortheast3 = "ap-northeast-3"
42-
43-
/// Asia Pacific (Singapore)
44-
case apSoutheast1 = "ap-southeast-1"
45-
46-
/// Asia Pacific (Sydney)
47-
case apSoutheast2 = "ap-southeast-2"
48-
49-
/// Asia Pacific (Mumbai)
50-
case apSouth1 = "ap-south-1"
51-
52-
/// South America (São Paulo)
53-
case saEast1 = "sa-east-1"
5+
public struct Region {
546

7+
/// name of the region, see RegionName
8+
public let name: RegionName
9+
10+
/// name of the custom host, can contain IP and/or port (e.g. 127.0.0.1:9000)
11+
public let hostName: String?
12+
13+
/// use TLS/https (defaults to true)
14+
public let useTLS: Bool
15+
16+
public enum RegionName : String, Codable {
17+
/// US East (N. Virginia)
18+
case usEast1 = "us-east-1"
19+
20+
/// US East (Ohio)
21+
case usEast2 = "us-east-2"
22+
23+
/// US West (N. California)
24+
case usWest1 = "us-west-1"
25+
26+
/// US West (Oregon)
27+
case usWest2 = "us-west-2"
28+
29+
/// Canada (Central)
30+
case caCentral1 = "ca-central-1"
31+
32+
/// EU (Frankfurt)
33+
case euCentral1 = "eu-central-1"
34+
35+
/// EU (Ireland)
36+
case euWest1 = "eu-west-1"
37+
38+
/// EU (London)
39+
case euWest2 = "eu-west-2"
40+
41+
/// EU (Paris)
42+
case euWest3 = "eu-west-3"
43+
44+
/// Asia Pacific (Tokyo)
45+
case apNortheast1 = "ap-northeast-1"
46+
47+
/// Asia Pacific (Seoul)
48+
case apNortheast2 = "ap-northeast-2"
49+
50+
/// Asia Pacific (Osaka-Local)
51+
case apNortheast3 = "ap-northeast-3"
52+
53+
/// Asia Pacific (Singapore)
54+
case apSoutheast1 = "ap-southeast-1"
55+
56+
/// Asia Pacific (Sydney)
57+
case apSoutheast2 = "ap-southeast-2"
58+
59+
/// Asia Pacific (Mumbai)
60+
case apSouth1 = "ap-south-1"
61+
62+
/// South America (São Paulo)
63+
case saEast1 = "sa-east-1"
64+
}
65+
66+
/// initializer for a (custom) region. If you use a custom hostName, you
67+
/// still need a region (e.g. use usEast1 for Minio)
68+
public init(name: RegionName, hostName: String? = nil, useTLS: Bool = true) {
69+
self.name = name
70+
self.hostName = hostName
71+
self.useTLS = useTLS
72+
}
5573
}
5674

5775

5876
extension Region {
5977

6078
/// Base URL / Host
6179
public var host: String {
62-
return "s3.\(rawValue).amazonaws.com"
80+
if let host = hostName {
81+
return host
82+
}
83+
return "s3.\(name.rawValue).amazonaws.com"
84+
}
85+
}
86+
87+
extension Region {
88+
public init?(rawValue: String) {
89+
guard let name = RegionName(rawValue: rawValue) else {
90+
return nil
91+
}
92+
93+
self.init(name: name)
94+
}
95+
96+
/// convenience var for US East (N. Virginia)
97+
public static var usEast1: Region {
98+
return Region(name: RegionName.usEast1)
99+
}
100+
101+
/// convenience var for US East (Ohio)
102+
public static var usEast2: Region {
103+
return Region(name: RegionName.usEast2)
104+
}
105+
106+
/// convenience var for US West (N. California)
107+
public static var usWest1: Region {
108+
return Region(name: RegionName.usWest1)
109+
}
110+
111+
/// convenience var for US West (Oregon)
112+
public static var usWest2: Region {
113+
return Region(name: RegionName.usWest2)
114+
}
115+
116+
/// convenience var for Canada (Central)
117+
public static var caCentral1: Region {
118+
return Region(name: RegionName.caCentral1)
119+
}
120+
121+
/// convenience var for EU (Frankfurt)
122+
public static var euCentral1: Region {
123+
return Region(name: RegionName.euCentral1)
124+
}
125+
126+
/// convenience var for EU (Ireland)
127+
public static var euWest1: Region {
128+
return Region(name: RegionName.euWest1)
129+
}
130+
131+
/// convenience var for EU (London)
132+
public static var euWest2: Region {
133+
return Region(name: RegionName.euWest2)
134+
}
135+
136+
/// convenience var for EU (Paris)
137+
public static var euWest3: Region {
138+
return Region(name: RegionName.euWest3)
139+
}
140+
141+
/// convenience var for Asia Pacific (Tokyo)
142+
public static var apNortheast1: Region {
143+
return Region(name: RegionName.apNortheast1)
144+
}
145+
146+
/// convenience var for Asia Pacific (Seoul)
147+
public static var apNortheast2: Region {
148+
return Region(name: RegionName.apNortheast2)
149+
}
150+
151+
/// convenience var for Asia Pacific (Osaka-Local)
152+
public static var apNortheast3: Region {
153+
return Region(name: RegionName.apNortheast3)
154+
}
155+
156+
/// convenience var for Asia Pacific (Singapore)
157+
public static var apSoutheast1: Region {
158+
return Region(name: RegionName.apSoutheast1)
159+
}
160+
161+
/// convenience var for Asia Pacific (Sydney)
162+
public static var apSoutheast2: Region {
163+
return Region(name: RegionName.apSoutheast2)
164+
}
165+
166+
/// convenience var for Asia Pacific (Mumbai)
167+
public static var apSouth1: Region {
168+
return Region(name: RegionName.apSouth1)
169+
}
170+
171+
/// convenience var for South America (São Paulo)
172+
public static var saEast1: Region {
173+
return Region(name: RegionName.saEast1)
174+
}
175+
}
176+
177+
/// Codable support for Region
178+
extension Region: Codable {
179+
180+
/// decodes a string (see RegionName) to a Region (does not support custom hosts)
181+
public init(from decoder: Decoder) throws {
182+
let container = try decoder.singleValueContainer()
183+
184+
let name = try container.decode(String.self)
185+
186+
guard let regionName = RegionName(rawValue: name) else {
187+
throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: [],
188+
debugDescription: "Could not find region for \(name)"))
189+
}
190+
191+
self.name = regionName
192+
self.hostName = nil
193+
self.useTLS = true
63194
}
64195

196+
/// encodes the name (see RegionName, does not support custom hosts)
197+
public func encode(to encoder: Encoder) throws {
198+
var container = encoder.singleValueContainer()
199+
try container.encode(self.name.rawValue)
200+
}
65201
}

Sources/S3Signer/S3Signer+Private.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ extension S3Signer {
3131

3232
func createSignature(_ stringToSign: String, timeStampShort: String, region: Region) throws -> String {
3333
let dateKey = try HMAC.SHA256.authenticate(timeStampShort.convertToData(), key: "AWS4\(config.secretKey)".convertToData())
34-
let dateRegionKey = try HMAC.SHA256.authenticate(region.rawValue.convertToData(), key: dateKey)
34+
let dateRegionKey = try HMAC.SHA256.authenticate(region.name.rawValue.convertToData(), key: dateKey)
3535
let dateRegionServiceKey = try HMAC.SHA256.authenticate(config.service.convertToData(), key: dateRegionKey)
3636
let signingKey = try HMAC.SHA256.authenticate("aws4_request".convertToData(), key: dateRegionServiceKey)
3737
let signature = try HMAC.SHA256.authenticate(stringToSign.convertToData(), key: signingKey)
@@ -44,8 +44,8 @@ extension S3Signer {
4444
}
4545

4646
func credentialScope(_ timeStampShort: String, region: Region) -> String {
47-
var arr = [timeStampShort, region.rawValue, config.service, "aws4_request"]
48-
if region == .none {
47+
var arr = [timeStampShort, region.name.rawValue, config.service, "aws4_request"]
48+
if region.name == .none {
4949
arr.remove(at: 1)
5050
}
5151
return arr.joined(separator: "/")

0 commit comments

Comments
 (0)