diff --git a/Package.resolved b/Package.resolved
index 04619ad..8b9264d 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -123,8 +123,8 @@
"repositoryURL": "https://github.com/vapor/url-encoded-form.git",
"state": {
"branch": null,
- "revision": "57cf7fb9c1a1014c50bc05123684a9139ad44127",
- "version": "1.0.3"
+ "revision": "cbfe7ef6301557d3f2d0807a98165232ae06e1c6",
+ "version": "1.0.4"
}
},
{
@@ -149,9 +149,9 @@
"package": "VaporTestTools",
"repositoryURL": "https://github.com/LiveUI/VaporTestTools.git",
"state": {
- "branch": "master",
+ "branch": null,
"revision": "e07ce263257463f2f2af9e640f81eb95a72760e4",
- "version": null
+ "version": "0.1.5"
}
},
{
diff --git a/Sources/S3/Extensions/S3+Bucket.swift b/Sources/S3/Extensions/S3+Bucket.swift
index f09c7cb..ac78654 100644
--- a/Sources/S3/Extensions/S3+Bucket.swift
+++ b/Sources/S3/Extensions/S3+Bucket.swift
@@ -74,7 +74,7 @@ public extension S3 {
let content = """
- \(region.rawValue)
+ \(region.name.rawValue)
"""
diff --git a/Sources/S3/Extensions/S3+List.swift b/Sources/S3/Extensions/S3+List.swift
index 7386a81..51be8e8 100644
--- a/Sources/S3/Extensions/S3+List.swift
+++ b/Sources/S3/Extensions/S3+List.swift
@@ -14,7 +14,7 @@ extension S3 {
/// Get list of objects
public func list(bucket: String, region: Region? = nil, headers: [String: String], on container: Container) throws -> Future {
let region = region ?? signer.config.region
- guard let baseUrl = URL(string: "https://\(bucket).s3.\(region.rawValue).amazonaws.com/"), let host = baseUrl.host,
+ guard let baseUrl = URL(string: region.hostUrlString(bucket: bucket)), let host = baseUrl.host,
var components = URLComponents(string: baseUrl.absoluteString) else {
throw S3.Error.invalidUrl
}
diff --git a/Sources/S3/URLBuilder/URLBuilder.swift b/Sources/S3/URLBuilder/URLBuilder.swift
index 49a6390..67dfea5 100644
--- a/Sources/S3/URLBuilder/URLBuilder.swift
+++ b/Sources/S3/URLBuilder/URLBuilder.swift
@@ -14,13 +14,22 @@ extension Region {
/// Host URL including scheme
public func hostUrlString(bucket: String? = nil) -> String {
+ if hostName != nil {
+ return urlProtocol + host.finished(with: "/") + (bucket ?? "")
+ }
+
var bucket = bucket
- if let b = bucket {
+ if let b = bucket, !b.isEmpty {
bucket = b + "."
}
- return "https://" + (bucket ?? "") + host.finished(with: "/")
+
+ return urlProtocol + (bucket ?? "") + host.finished(with: "/")
}
+ private var urlProtocol: String {
+ return useTLS ? "https://" : "http://"
+ }
+
}
diff --git a/Sources/S3Signer/Region.swift b/Sources/S3Signer/Region.swift
index 46a3aff..a32ac1f 100755
--- a/Sources/S3Signer/Region.swift
+++ b/Sources/S3Signer/Region.swift
@@ -2,56 +2,74 @@ import Foundation
/// AWS Region
-public enum Region: String, Codable {
-
- /// US East (N. Virginia)
- case usEast1 = "us-east-1"
-
- /// US East (Ohio)
- case usEast2 = "us-east-2"
-
- /// US West (N. California)
- case usWest1 = "us-west-1"
-
- /// US West (Oregon)
- case usWest2 = "us-west-2"
-
- /// Canada (Central)
- case caCentral1 = "ca-central-1"
-
- /// EU (Frankfurt)
- case euCentral1 = "eu-central-1"
-
- /// EU (Ireland)
- case euWest1 = "eu-west-1"
-
- /// EU (London)
- case euWest2 = "eu-west-2"
-
- /// EU (Paris)
- case euWest3 = "eu-west-3"
-
- /// Asia Pacific (Tokyo)
- case apNortheast1 = "ap-northeast-1"
-
- /// Asia Pacific (Seoul)
- case apNortheast2 = "ap-northeast-2"
-
- /// Asia Pacific (Osaka-Local)
- case apNortheast3 = "ap-northeast-3"
-
- /// Asia Pacific (Singapore)
- case apSoutheast1 = "ap-southeast-1"
-
- /// Asia Pacific (Sydney)
- case apSoutheast2 = "ap-southeast-2"
-
- /// Asia Pacific (Mumbai)
- case apSouth1 = "ap-south-1"
-
- /// South America (São Paulo)
- case saEast1 = "sa-east-1"
+public struct Region {
+ /// name of the region, see RegionName
+ public let name: RegionName
+
+ /// name of the custom host, can contain IP and/or port (e.g. 127.0.0.1:9000)
+ public let hostName: String?
+
+ /// use TLS/https (defaults to true)
+ public let useTLS: Bool
+
+ public enum RegionName : String, Codable {
+ /// US East (N. Virginia)
+ case usEast1 = "us-east-1"
+
+ /// US East (Ohio)
+ case usEast2 = "us-east-2"
+
+ /// US West (N. California)
+ case usWest1 = "us-west-1"
+
+ /// US West (Oregon)
+ case usWest2 = "us-west-2"
+
+ /// Canada (Central)
+ case caCentral1 = "ca-central-1"
+
+ /// EU (Frankfurt)
+ case euCentral1 = "eu-central-1"
+
+ /// EU (Ireland)
+ case euWest1 = "eu-west-1"
+
+ /// EU (London)
+ case euWest2 = "eu-west-2"
+
+ /// EU (Paris)
+ case euWest3 = "eu-west-3"
+
+ /// Asia Pacific (Tokyo)
+ case apNortheast1 = "ap-northeast-1"
+
+ /// Asia Pacific (Seoul)
+ case apNortheast2 = "ap-northeast-2"
+
+ /// Asia Pacific (Osaka-Local)
+ case apNortheast3 = "ap-northeast-3"
+
+ /// Asia Pacific (Singapore)
+ case apSoutheast1 = "ap-southeast-1"
+
+ /// Asia Pacific (Sydney)
+ case apSoutheast2 = "ap-southeast-2"
+
+ /// Asia Pacific (Mumbai)
+ case apSouth1 = "ap-south-1"
+
+ /// South America (São Paulo)
+ case saEast1 = "sa-east-1"
+ }
+
+ /// initializer for a (custom) region. If you use a custom hostName, you
+ /// still need a region (e.g. use usEast1 for Minio)
+ public init(name: RegionName, hostName: String? = nil, useTLS: Bool = true) {
+ self.name = name
+ self.hostName = hostName
+ self.useTLS = useTLS
+ }
}
@@ -59,7 +77,125 @@ extension Region {
/// Base URL / Host
public var host: String {
- return "s3.\(rawValue).amazonaws.com"
+ if let host = hostName {
+ return host
+ }
+ return "s3.\(name.rawValue).amazonaws.com"
+ }
+}
+
+extension Region {
+ public init?(rawValue: String) {
+ guard let name = RegionName(rawValue: rawValue) else {
+ return nil
+ }
+
+ self.init(name: name)
+ }
+
+ /// convenience var for US East (N. Virginia)
+ public static var usEast1: Region {
+ return Region(name: RegionName.usEast1)
+ }
+
+ /// convenience var for US East (Ohio)
+ public static var usEast2: Region {
+ return Region(name: RegionName.usEast2)
+ }
+
+ /// convenience var for US West (N. California)
+ public static var usWest1: Region {
+ return Region(name: RegionName.usWest1)
+ }
+
+ /// convenience var for US West (Oregon)
+ public static var usWest2: Region {
+ return Region(name: RegionName.usWest2)
+ }
+
+ /// convenience var for Canada (Central)
+ public static var caCentral1: Region {
+ return Region(name: RegionName.caCentral1)
+ }
+
+ /// convenience var for EU (Frankfurt)
+ public static var euCentral1: Region {
+ return Region(name: RegionName.euCentral1)
+ }
+
+ /// convenience var for EU (Ireland)
+ public static var euWest1: Region {
+ return Region(name: RegionName.euWest1)
+ }
+
+ /// convenience var for EU (London)
+ public static var euWest2: Region {
+ return Region(name: RegionName.euWest2)
+ }
+
+ /// convenience var for EU (Paris)
+ public static var euWest3: Region {
+ return Region(name: RegionName.euWest3)
+ }
+
+ /// convenience var for Asia Pacific (Tokyo)
+ public static var apNortheast1: Region {
+ return Region(name: RegionName.apNortheast1)
+ }
+
+ /// convenience var for Asia Pacific (Seoul)
+ public static var apNortheast2: Region {
+ return Region(name: RegionName.apNortheast2)
+ }
+
+ /// convenience var for Asia Pacific (Osaka-Local)
+ public static var apNortheast3: Region {
+ return Region(name: RegionName.apNortheast3)
+ }
+
+ /// convenience var for Asia Pacific (Singapore)
+ public static var apSoutheast1: Region {
+ return Region(name: RegionName.apSoutheast1)
+ }
+
+ /// convenience var for Asia Pacific (Sydney)
+ public static var apSoutheast2: Region {
+ return Region(name: RegionName.apSoutheast2)
+ }
+
+ /// convenience var for Asia Pacific (Mumbai)
+ public static var apSouth1: Region {
+ return Region(name: RegionName.apSouth1)
+ }
+
+ /// convenience var for South America (São Paulo)
+ public static var saEast1: Region {
+ return Region(name: RegionName.saEast1)
+ }
+}
+
+/// Codable support for Region
+extension Region: Codable {
+
+ /// decodes a string (see RegionName) to a Region (does not support custom hosts)
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.singleValueContainer()
+
+ let name = try container.decode(String.self)
+
+ guard let regionName = RegionName(rawValue: name) else {
+ throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: [],
+ debugDescription: "Could not find region for \(name)"))
+ }
+
+ self.name = regionName
+ self.hostName = nil
+ self.useTLS = true
}
+ /// encodes the name (see RegionName, does not support custom hosts)
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+ try container.encode(self.name.rawValue)
+ }
}
diff --git a/Sources/S3Signer/S3Signer+Private.swift b/Sources/S3Signer/S3Signer+Private.swift
index dd696a9..67a3798 100755
--- a/Sources/S3Signer/S3Signer+Private.swift
+++ b/Sources/S3Signer/S3Signer+Private.swift
@@ -31,7 +31,7 @@ extension S3Signer {
func createSignature(_ stringToSign: String, timeStampShort: String, region: Region) throws -> String {
let dateKey = try HMAC.SHA256.authenticate(timeStampShort.convertToData(), key: "AWS4\(config.secretKey)".convertToData())
- let dateRegionKey = try HMAC.SHA256.authenticate(region.rawValue.convertToData(), key: dateKey)
+ let dateRegionKey = try HMAC.SHA256.authenticate(region.name.rawValue.convertToData(), key: dateKey)
let dateRegionServiceKey = try HMAC.SHA256.authenticate(config.service.convertToData(), key: dateRegionKey)
let signingKey = try HMAC.SHA256.authenticate("aws4_request".convertToData(), key: dateRegionServiceKey)
let signature = try HMAC.SHA256.authenticate(stringToSign.convertToData(), key: signingKey)
@@ -44,8 +44,8 @@ extension S3Signer {
}
func credentialScope(_ timeStampShort: String, region: Region) -> String {
- var arr = [timeStampShort, region.rawValue, config.service, "aws4_request"]
- if region == .none {
+ var arr = [timeStampShort, region.name.rawValue, config.service, "aws4_request"]
+ if region.name == .none {
arr.remove(at: 1)
}
return arr.joined(separator: "/")