diff --git a/Sources/Driver/Authentication/PercentEncoder.swift b/Sources/Driver/Authentication/PercentEncoder.swift index 03030a1..e6a2dc8 100644 --- a/Sources/Driver/Authentication/PercentEncoder.swift +++ b/Sources/Driver/Authentication/PercentEncoder.swift @@ -1,13 +1,13 @@ import Core extension Byte { - static let awsQueryAllowed = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~=".bytes + public static let awsQueryAllowed = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~=".bytes - static let awsPathAllowed = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~/".bytes + public static let awsPathAllowed = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~/".bytes } extension String { - func percentEncode(allowing allowed: Bytes) throws -> String { + public func percentEncode(allowing allowed: Bytes) throws -> String { let bytes = self.bytes let encodedBytes = try percentEncodedUppercase(bytes, shouldEncode: { return !allowed.contains($0) diff --git a/Sources/S3/S3.swift b/Sources/S3/S3.swift index 13d272a..bba57fa 100644 --- a/Sources/S3/S3.swift +++ b/Sources/S3/S3.swift @@ -4,60 +4,73 @@ import Driver import Transport public struct S3 { - let signer: AWSSignatureV4? - let bucket: String - let isVirtualHosted: Bool + public enum Error: Swift.Error { + case unimplemented + case invalidResponse(Status, String?) + } + + let signer: AWSSignatureV4 + public var host: String public init( + host: String, accessKey: String, secretKey: String, - region: Region, - bucket: String, - isBucketVirtualHosted: Bool = false + region: Region ) { - signer = nil - self.bucket = bucket - self.isVirtualHosted = isBucketVirtualHosted + self.host = host + signer = AWSSignatureV4( + service: "s3", + host: host, + region: region, + accessKey: accessKey, + secretKey: secretKey + ) } public func upload(bytes: Bytes, path: String, access: AccessControlList) throws { + let url = generateURL(for: path) + let headers = try signer.sign( + payload: .bytes(bytes), + method: .put, + path: path + //TODO(Brett): headers & AccessControlList + ) + + let response = try BasicClient.put(url, headers: headers, body: Body.data(bytes)) + guard response.status == .ok else { + throw Error.invalidResponse(response.status, response.body.bytes?.string) + } } - public func get(path: String) throws -> Response { + public func get(path: String) throws -> Bytes { let url = generateURL(for: path) + let headers = try signer.sign(path: path) - /* - let headers = try s3Signer.authHeaderV4( - httpMethod: .get, - urlString: url, - headers: [:], - payload: .none - ).vaporHeaders - */ + let response = try BasicClient.get(url, headers: headers) + guard response.status == .ok else { + throw Error.invalidResponse(response.status, response.body.bytes?.string) + } - return try BasicClient.get(url, headers: [:]) - } - - public func exist(file: String) { - + guard let bytes = response.body.bytes else { + throw Error.invalidResponse( + .internalServerError, + "Response from S3 did not contain a body." + ) + } + + return bytes } - public func delete(file: String) { - + public func delete(file: String) throws { + throw Error.unimplemented } } extension S3 { func generateURL(for path: String) -> String { - var url: String - - if isVirtualHosted { - url = "https://\(bucket).s3.amazonaws.com/" - } else { - url = "https://s3.amazonaws.com/\(bucket)/" - } - - return url + path + //FIXME(Brett): + return "https://\(host)\(path)" } } diff --git a/Tests/AWSTests/AWSTests.swift b/Tests/AWSTests/AWSTests.swift index dbc38f4..5420a4d 100644 --- a/Tests/AWSTests/AWSTests.swift +++ b/Tests/AWSTests/AWSTests.swift @@ -1,6 +1,7 @@ import XCTest import HTTP import Transport +@testable import S3 @testable import Driver class AWSTests: XCTestCase {