amplify-swift/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Internal/URLComponents+sigv4Encoding...

58 lines
2.5 KiB
Swift

//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Foundation
import Amplify
/// Per AWS reference guide https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
/// URL querystring should be encoded according to the following rules:
/// - percent-encode with %XY (X and Y are hexadecimal characters) all characters
/// but any of the __unreserved characters__ defined by `RFC3986` (A-Za-z0-9-_.~)
/// - encode spaces with %20
/// - double encode any equals in params values
///
/// `URLComponents` encodes queryItems values by strictly following `RFC 3986`.
extension URLComponents {
static var sigV4UnreservedCharacters: CharacterSet = {
var sigV4UnreservedCharacters = CharacterSet(charactersIn: "A" ... "Z")
sigV4UnreservedCharacters = sigV4UnreservedCharacters.union(CharacterSet(charactersIn: "a" ... "z"))
sigV4UnreservedCharacters = sigV4UnreservedCharacters.union(CharacterSet(charactersIn: "0" ... "9"))
sigV4UnreservedCharacters = sigV4UnreservedCharacters.union(CharacterSet(charactersIn: "-_~."))
return sigV4UnreservedCharacters
}()
private func encodeQueryParamItemBySigV4Rules(_ value: String) -> String? {
// removingPercentEncoding returns `nil` if called on a value
// that hasn't been prior encoded
let unencoded = value.removingPercentEncoding ?? value
return unencoded.addingPercentEncoding(
withAllowedCharacters: Self.sigV4UnreservedCharacters)
}
/// Encodes query items per SigV4 rules, no-op for already encoded items.
mutating func encodeQueryItemsPerSigV4Rules(_ queryItems: [String: String]?) throws {
guard let queryItems = queryItems else {
return
}
percentEncodedQuery = try queryItems.map { name, value in
guard let encodedName = encodeQueryParamItemBySigV4Rules(name),
let encodedValue = encodeQueryParamItemBySigV4Rules(value) else {
throw APIError.invalidURL(
"Invalid query parameter.",
"""
Review your Amplify.API call to make sure you are passing \
valid UTF-8 query parameters in your request.
The value passed was '\(name)=\(value)'
""")
}
return [encodedName, encodedValue].joined(separator: "=")
}.joined(separator: "&")
}
}