├── .dockerignore ├── .github ├── FUNDING.yml ├── workflows │ ├── sanity.yml │ ├── benchmark.yml │ ├── gen-docs.yml │ └── ci.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── Benchmark ├── README.md ├── Package.swift └── Sources │ └── soto-benchmark │ ├── main.swift │ ├── AWSSignerV4Suite.swift │ └── AWSClientSuite.swift ├── .gitignore ├── Sources ├── CSotoExpat │ ├── AUTHORS │ ├── COPYING │ ├── winconfig.h │ ├── libexpat.def │ ├── libexpatw.def │ ├── Makefile.am │ ├── asciitab.h │ ├── utf8tab.h │ ├── xmltok_impl.h │ ├── latin1tab.h │ ├── iasciitab.h │ ├── ascii.h │ ├── expat_config.h │ ├── internal.h │ └── xmltok_ns.c ├── SotoCrypto │ ├── exports.swift │ ├── Insecure.swift │ ├── Digest.swift │ ├── SymmetricKey.swift │ ├── ByteArray.swift │ ├── MD5.swift │ ├── HMAC.swift │ ├── HashFunction.swift │ └── SHA2.swift ├── SotoSignerV4 │ ├── exports.swift │ └── credentials.swift ├── SotoCore │ ├── Credential │ │ ├── Credential+IsEmpty.swift │ │ ├── StaticCredential+CredentialProvider.swift │ │ ├── NullCredentialProvider.swift │ │ ├── CredentialProviderError.swift │ │ ├── StaticCredential+Environment.swift │ │ ├── ExpiringCredential.swift │ │ ├── CredentialProviderSelector.swift │ │ ├── RuntimeSelectorCredentialProvider.swift │ │ ├── DeferredCredentialProvider.swift │ │ ├── RotatingCredentialProvider.swift │ │ └── ConfigFileCredentialProvider.swift │ ├── Doc │ │ ├── Environment.swift │ │ ├── Mirror.swift │ │ ├── ServiceProtocol.swift │ │ ├── AWSMemberEncoding.swift │ │ └── AWSShape+Encoder.swift │ ├── Exports.swift │ ├── Message │ │ ├── AWSResponse+HAL.swift │ │ ├── Body.swift │ │ └── AWSMiddleware.swift │ ├── Errors │ │ ├── ServerErrors.swift │ │ └── Error.swift │ ├── HTTP │ │ ├── AWSHTTPClient.swift │ │ ├── ResponseDelegate.swift │ │ ├── StreamReader.swift │ │ ├── AsyncHTTPClient.swift │ │ └── StreamWriter+write.swift │ ├── Encoder │ │ └── CodableProperties │ │ │ └── DateCoders.swift │ └── AWSService.swift └── SotoTestUtils │ ├── Environment.swift │ └── TestUtils.swift ├── .jazzy.yaml ├── .mailmap ├── Dockerfile ├── .swiftformat ├── Tests ├── LinuxMain.swift ├── SotoCoreTests │ ├── Doc │ │ └── EnvironmentTests.swift │ ├── Credential │ │ └── StaticCredential+EnvironmentTests.swift │ ├── PayloadTests.swift │ └── AWSServiceTests.swift └── INIParserTests │ └── INIParserTests.swift ├── scripts ├── generate-docs.sh ├── build-docs.sh ├── commit-docs.sh ├── generate-contributors-list.sh ├── expat-symbols.sh ├── templates │ ├── generate-region │ │ ├── Region-Tests.stencil │ │ └── Region.stencil │ └── generate-errors │ │ └── generate-errors.stencil ├── generate-region-test.swift ├── generate-region.swift └── sanity.sh ├── README.md ├── CONTRIBUTORS.txt ├── NOTICE.txt ├── SECURITY.md ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md └── Package.swift /.dockerignore: -------------------------------------------------------------------------------- 1 | .build 2 | .git -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: adam-fowler 2 | -------------------------------------------------------------------------------- /Benchmark/README.md: -------------------------------------------------------------------------------- 1 | # soto-benchmark 2 | 3 | Benchmark testing for soto-core 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .build 3 | .swiftpm 4 | /Packages 5 | /*.xcodeproj 6 | Package.resolved 7 | /build 8 | /docs 9 | 10 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/AUTHORS: -------------------------------------------------------------------------------- 1 | Expat is brought to you by: 2 | 3 | Clark Cooper 4 | Fred L. Drake, Jr. 5 | Greg Stein 6 | James Clark 7 | Karl Waclawek 8 | Rhodri James 9 | Sebastian Pipping 10 | Steven Solie 11 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | sourcekitten_sourcefile: 2 | - sourcekitten/SotoCore.json 3 | author_url: https://github.com/soto-project 4 | github_url: https://github.com/soto-project/soto-core 5 | copyright: '© Copyright (c) 2020 Soto project authors' 6 | readme: README.md 7 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Jonathan McAllister 2 | Jonathan McAllister 3 | Jonathan McAllister Jonny McAllister 4 | Yuki Takei Yuki Takei 5 | Yuki Takei noppoman 6 | Roland Möller Ro-M 7 | -------------------------------------------------------------------------------- /.github/workflows/sanity.yml: -------------------------------------------------------------------------------- 1 | name: Sanity Check 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | sanity-check: 10 | runs-on: macOS-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v1 14 | with: 15 | fetch-depth: 1 16 | - name: Install Dependencies 17 | run: | 18 | brew install mint 19 | mint install nicklockwood/swiftformat@0.47.13 --no-link 20 | - name: run script 21 | run: ./scripts/sanity.sh 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # ================================ 2 | # Build image 3 | # ================================ 4 | FROM swift:5.4 as build 5 | 6 | WORKDIR /build 7 | 8 | # First just resolve dependencies. 9 | # This creates a cached layer that can be reused 10 | # as long as your Package.swift/Package.resolved 11 | # files do not change. 12 | COPY ./Package.* ./ 13 | RUN swift package resolve 14 | 15 | # Copy entire repo into container 16 | COPY . . 17 | 18 | RUN swift test --enable-test-discovery --sanitize=thread 19 | 20 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | # Minimum swiftformat version 2 | --minversion 0.47.4 3 | 4 | # Swift version 5 | --swiftversion 5.1 6 | 7 | # file options 8 | --exclude .build 9 | 10 | # rules 11 | --disable redundantReturn, extensionAccessControl 12 | 13 | # format options 14 | --ifdef no-indent 15 | --nospaceoperators ...,..< 16 | --patternlet inline 17 | --self insert 18 | --stripunusedargs unnamed-only 19 | 20 | #--maxwidth 150 21 | --wraparguments before-first 22 | --wrapparameters before-first 23 | --wrapcollections before-first 24 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | -------------------------------------------------------------------------------- /Sources/SotoCrypto/exports.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // Replicating the CryptoKit framework interface for < macOS 10.15 16 | 17 | #if os(Linux) 18 | @_exported import Crypto 19 | #endif 20 | -------------------------------------------------------------------------------- /scripts/generate-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Soto for AWS open source project 5 | ## 6 | ## Copyright (c) 2020 the Soto project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## See CONTRIBUTORS.txt for the list of Soto project authors 11 | ## 12 | ## SPDX-License-Identifier: Apache-2.0 13 | ## 14 | ##===----------------------------------------------------------------------===## 15 | 16 | set -eux 17 | 18 | DIRNAME=$(dirname "$0") 19 | 20 | source "$DIRNAME"/build-docs.sh 21 | source "$DIRNAME"/commit-docs.sh 22 | 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for AWSSDKSwift 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /Sources/SotoCrypto/Insecure.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // Replicating the CryptoKit framework interface for < macOS 10.15 16 | 17 | #if !os(Linux) 18 | 19 | public enum Insecure {} 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /Benchmark/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "soto-benchmark", 8 | dependencies: [ 9 | .package(url: "https://github.com/soto-project/soto-core", .branch("main")), 10 | .package(name: "Benchmark", url: "https://github.com/google/swift-benchmark", from: "0.1.0"), 11 | ], 12 | targets: [ 13 | .target(name: "soto-benchmark", dependencies: [ 14 | .product(name: "SotoCore", package: "soto-core"), 15 | .product(name: "Benchmark", package: "Benchmark"), 16 | ]), 17 | ] 18 | ) 19 | -------------------------------------------------------------------------------- /Sources/SotoSignerV4/exports.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | @_exported import struct NIO.ByteBuffer 16 | @_exported import struct NIO.ByteBufferAllocator 17 | @_exported import struct NIO.TimeAmount 18 | 19 | @_exported import NIOHTTP1 20 | -------------------------------------------------------------------------------- /Sources/SotoCore/Credential/Credential+IsEmpty.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import SotoSignerV4 16 | 17 | extension Credential { 18 | func isEmpty() -> Bool { 19 | return self.accessKeyId.isEmpty || self.secretAccessKey.isEmpty 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Benchmark/Sources/soto-benchmark/main.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Benchmark 16 | 17 | let suites = [ 18 | awsSignerV4Suite, 19 | queryEncoderSuite, 20 | xmlEncoderSuite, 21 | xmlDecoderSuite, 22 | dictionaryDecoderSuite, 23 | awsClientSuite, 24 | ] 25 | 26 | Benchmark.main(suites) 27 | -------------------------------------------------------------------------------- /Tests/SotoCoreTests/Doc/EnvironmentTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | @testable import SotoCore 16 | import XCTest 17 | 18 | class EnvironmentTests: XCTestCase { 19 | func testGetPathFromEnvironment() { 20 | let path = Environment["PATH"] 21 | XCTAssertNotNil(path) 22 | XCTAssertNotEqual(path, "") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve the functionality of AWSSDKSwift! 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. If you can include a link to redacted requests or responses, that would be very helpful as well! 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. ... 16 | 2. ... 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Setup (please complete the following information):** 22 | - OS: [e.g. iOS] 23 | - Version of aws-sdk-swift [e.g. 3.0] 24 | - Authentication mechanism [hard-coded credentials, IAM Instance Profile on EC2, etc] 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /scripts/build-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Soto for AWS open source project 5 | ## 6 | ## Copyright (c) 2020 the Soto project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## See CONTRIBUTORS.txt for the list of Soto project authors 11 | ## 12 | ## SPDX-License-Identifier: Apache-2.0 13 | ## 14 | ##===----------------------------------------------------------------------===## 15 | 16 | set -eux 17 | 18 | # make temp directory 19 | mkdir -p sourcekitten 20 | 21 | # generate source kitten json 22 | sourcekitten doc --spm --module-name "SotoCore" > sourcekitten/SotoCore.json; 23 | 24 | # generate documentation with jazzy 25 | jazzy --clean 26 | 27 | # tidy up 28 | rm -rf sourcekitten 29 | rm -rf docs/docsets 30 | -------------------------------------------------------------------------------- /Sources/SotoCore/Doc/Environment.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | #if os(Linux) 16 | import Glibc 17 | #else 18 | import Darwin.C 19 | #endif 20 | 21 | internal enum Environment { 22 | internal static subscript(_ name: String) -> String? { 23 | guard let value = getenv(name) else { 24 | return nil 25 | } 26 | return String(cString: value) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/SotoTestUtils/Environment.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | #if os(Linux) 16 | import Glibc 17 | #else 18 | import Darwin.C 19 | #endif 20 | 21 | internal enum Environment { 22 | internal static subscript(_ name: String) -> String? { 23 | guard let value = getenv(name) else { 24 | return nil 25 | } 26 | return String(cString: value) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/SotoCore/Credential/StaticCredential+CredentialProvider.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Logging 16 | import SotoSignerV4 17 | 18 | extension StaticCredential: CredentialProvider { 19 | /// Return static credential 20 | public func getCredential(on eventLoop: EventLoop, logger: Logger) -> EventLoopFuture { 21 | eventLoop.makeSucceededFuture(self) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/SotoCore/Credential/NullCredentialProvider.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Logging 16 | import NIO 17 | import SotoSignerV4 18 | 19 | /// Credential provider that always fails 20 | public struct NullCredentialProvider: CredentialProvider { 21 | public init() {} 22 | 23 | public func getCredential(on eventLoop: EventLoop, logger: Logger) -> EventLoopFuture { 24 | return eventLoop.makeFailedFuture(CredentialProviderError.noProvider) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/benchmark.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | macos: 11 | runs-on: macOS-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 1 17 | - name: Benchmark 18 | run: | 19 | cd Benchmark 20 | swift run -c release 21 | 22 | linux: 23 | runs-on: ubuntu-latest 24 | strategy: 25 | matrix: 26 | tag: 27 | - swift:5.2 28 | - swift:5.3 29 | - swift:5.4 30 | container: 31 | image: ${{ matrix.tag }} 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v2 35 | with: 36 | fetch-depth: 1 37 | - name: Install dependencies 38 | run: | 39 | apt-get update -qq 40 | apt-get install -q -y tzdata zlib1g-dev curl 41 | - name: Benchmark 42 | run: | 43 | cd Benchmark 44 | swift run -c release 45 | -------------------------------------------------------------------------------- /Sources/SotoCrypto/Digest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // Replicating the CryptoKit framework interface for < macOS 10.15 16 | 17 | #if !os(Linux) 18 | 19 | import protocol Foundation.ContiguousBytes 20 | 21 | /// Protocol for Digest object returned from HashFunction 22 | public protocol Digest: Sequence, ContiguousBytes, Hashable where Element == UInt8 { 23 | static var byteCount: Int { get } 24 | } 25 | 26 | /// Protocol for Digest object consisting of a byte array 27 | protocol ByteDigest: Digest, ByteArray {} 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /Sources/SotoCore/Doc/Mirror.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | func unwrap(_ any: Any) -> Any? { 16 | let mirror = Mirror(reflecting: any) 17 | guard mirror.displayStyle == .optional else { return any } 18 | guard let first = mirror.children.first else { return nil } 19 | return first.value 20 | } 21 | 22 | extension Mirror { 23 | func getAttribute(forKey key: String) -> Any? { 24 | guard let matched = children.filter({ $0.label == key }).first else { 25 | return nil 26 | } 27 | return unwrap(matched.value) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/SotoCore/Exports.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | @_exported import protocol SotoSignerV4.Credential 16 | @_exported import struct SotoSignerV4.StaticCredential 17 | 18 | @_exported import struct Logging.Logger 19 | 20 | @_exported import struct NIO.ByteBuffer 21 | @_exported import struct NIO.ByteBufferAllocator 22 | @_exported import protocol NIO.EventLoop 23 | @_exported import class NIO.EventLoopFuture 24 | @_exported import protocol NIO.EventLoopGroup 25 | @_exported import struct NIO.TimeAmount 26 | 27 | @_exported import struct NIOHTTP1.HTTPHeaders 28 | @_exported import enum NIOHTTP1.HTTPMethod 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Soto Core 2 | 3 | [Swift 5.1](https://swift.org) 4 | [](https://github.com/soto-project/soto-core/actions) 5 | [Codecov Result](https://codecov.io/gh/soto-project/soto-core) 6 | 7 | The core framework for [Soto](https://github.com/soto-project/soto) the Swift SDK for AWS. This is the underlying driver for executing requests to AWS, but you should be using one of the libraries provided by Soto instead of this! 8 | 9 | Documentation for the core library can be found [here](https://soto-project.github.io/soto-core). Documentation for Soto can be found [here](https://soto.codes). 10 | 11 | ## Contributing 12 | 13 | We welcome and encourage contributions from all developers. Please read [CONTRIBUTING.md](CONTRIBUTING.md) for our contributing guidelines. 14 | 15 | ## License 16 | 17 | `soto-core` is released under the Apache 2.0 license. See [LICENSE](LICENSE) for details. 18 | -------------------------------------------------------------------------------- /Sources/SotoCore/Credential/CredentialProviderError.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | public struct CredentialProviderError: Error, Equatable { 16 | enum _CredentialProviderError { 17 | case noProvider 18 | } 19 | 20 | let error: _CredentialProviderError 21 | 22 | public static var noProvider: CredentialProviderError { return .init(error: .noProvider) } 23 | } 24 | 25 | extension CredentialProviderError: CustomStringConvertible { 26 | public var description: String { 27 | switch self.error { 28 | case .noProvider: 29 | return "No credential provider found" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CONTRIBUTORS.txt: -------------------------------------------------------------------------------- 1 | For the purpose of tracking copyright, this is the list of individuals and 2 | organizations who have contributed source code to soto-core. 3 | 4 | For employees of an organization/company where the copyright of work done 5 | by employees of that company is held by the company itself, only the company 6 | needs to be listed here. 7 | 8 | ## COPYRIGHT HOLDERS 9 | 10 | ### Contributors 11 | 12 | - Adam Fowler 13 | - Eneko Alonso 14 | - Fabian Fett 15 | - Joe Smith 16 | - Jonathan McAllister 17 | - Kyle Ishie 18 | - Oliver O'Neill 19 | - Roland Möller 20 | - Sébastien Stormacq 21 | - Yifei Teng 22 | - Yuki Takei 23 | - giginet 24 | 25 | **Updating this list** 26 | 27 | Please do not edit this file manually. It is generated using `./scripts/generate_contributors_list.sh`. If a name is misspelled or appearing multiple times: add an entry in `./.mailmap` 28 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | This product contains a copy of INIParser from PerfectlySoft 16 | 17 | * LICENSE (Apache License 2.0): 18 | * https://github.com/PerfectlySoft/Perfect-INIParser/blob/master/LICENSE 19 | * HOMEPAGE: 20 | * https://github.com/PerfectlySoft/Perfect-INIParser 21 | 22 | This product contains code based on JSONDecoder from Apple 23 | 24 | * LICENSE (Apache License 2.0): 25 | * https://swift.org/LICENSE.txt 26 | * HOMEPAGE: 27 | * https://swift.org 28 | 29 | This product contains a copy of libexpat 30 | 31 | * LICENSE (MIT): 32 | * https://github.com/libexpat/libexpat/blob/master/expat/COPYING 33 | * HOMEPAGE: 34 | * https://libexpat.github.io/ 35 | -------------------------------------------------------------------------------- /Sources/SotoCore/Doc/ServiceProtocol.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | public enum ServiceProtocol { 16 | case json(version: String) 17 | case restjson 18 | case restxml 19 | case query 20 | case ec2 21 | } 22 | 23 | extension ServiceProtocol { 24 | public var contentType: String { 25 | switch self { 26 | case .json(let version): 27 | return "application/x-amz-json-\(version)" 28 | case .restjson: 29 | return "application/json" 30 | case .restxml: 31 | return "application/octet-stream" 32 | case .query, .ec2: 33 | return "application/x-www-form-urlencoded; charset=utf-8" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/gen-docs.yml: -------------------------------------------------------------------------------- 1 | name: Generate Documentation 2 | 3 | on: 4 | release: 5 | types: [published] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: macOS-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v1 16 | - name: Install Dependencies 17 | run: | 18 | brew install sourcekitten 19 | gem install jazzy 20 | - name: Build 21 | run: | 22 | ./scripts/build-docs.sh 23 | - name: Commit 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | REMOTE_BRANCH: "gh-pages" 27 | run: | 28 | REMOTE_REPO="https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" 29 | git config user.name "${GITHUB_ACTOR}" 30 | git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" 31 | git checkout ${REMOTE_BRANCH} 32 | rm -rf 5.x.x/ 33 | mv docs/ 5.x.x/ 34 | git add --all 5.x.x 35 | git status 36 | 37 | git commit -m "Documentation for https://github.com/${GITHUB_REPOSITORY}/tree/${GITHUB_SHA}" -m "Generated by gen-docs.yml" 38 | git push ${REMOTE_REPO} ${REMOTE_BRANCH} 39 | 40 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper 2 | Copyright (c) 2001-2019 Expat maintainers 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Sources/SotoCore/Doc/AWSMemberEncoding.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | /// Structure defining where to serialize member of AWSShape. 16 | public struct AWSMemberEncoding { 17 | /// Location of AWSMemberEncoding. 18 | public enum Location { 19 | case uri(locationName: String) 20 | case querystring(locationName: String) 21 | case header(locationName: String) 22 | case statusCode 23 | case body(locationName: String) 24 | } 25 | 26 | /// name of member 27 | public let label: String 28 | /// where to find or place member 29 | public let location: Location? 30 | 31 | public init(label: String, location: Location? = nil) { 32 | self.label = label 33 | self.location = location 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/SotoSignerV4/credentials.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | /// Protocol for providing credential details for accessing AWS services 16 | public protocol Credential { 17 | var accessKeyId: String { get } 18 | var secretAccessKey: String { get } 19 | var sessionToken: String? { get } 20 | } 21 | 22 | /// basic version of Credential where you supply the credentials 23 | public struct StaticCredential: Credential, Equatable { 24 | public let accessKeyId: String 25 | public let secretAccessKey: String 26 | public let sessionToken: String? 27 | 28 | public init(accessKeyId: String, secretAccessKey: String, sessionToken: String? = nil) { 29 | self.accessKeyId = accessKeyId 30 | self.secretAccessKey = secretAccessKey 31 | self.sessionToken = sessionToken 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /scripts/commit-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Soto for AWS open source project 5 | ## 6 | ## Copyright (c) 2020 the Soto project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## See CONTRIBUTORS.txt for the list of Soto project authors 11 | ## 12 | ## SPDX-License-Identifier: Apache-2.0 13 | ## 14 | ##===----------------------------------------------------------------------===## 15 | 16 | set -eux 17 | 18 | FOLDER=5.x.x 19 | 20 | # stash everything that isn't in docs, store result in STASH_RESULT 21 | STASH_RESULT=$(git stash push -- ":(exclude)docs") 22 | # get branch name 23 | CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) 24 | REVISION_HASH=$(git rev-parse HEAD) 25 | 26 | git checkout gh-pages 27 | # copy contents of docs to docs/current replacing the ones that are already there 28 | rm -rf "$FOLDER" 29 | mv docs/ "$FOLDER"/ 30 | # commit 31 | git add --all "$FOLDER" 32 | git commit -m "Documentation for https://github.com/soto-project/soto-core/tree/$REVISION_HASH" 33 | git push 34 | # return to branch 35 | git checkout $CURRENT_BRANCH 36 | 37 | if [ "$STASH_RESULT" != "No local changes to save" ]; then 38 | git stash pop 39 | fi 40 | 41 | -------------------------------------------------------------------------------- /Sources/SotoCrypto/SymmetricKey.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // Replicating the CryptoKit framework interface for < macOS 10.15 16 | 17 | #if !os(Linux) 18 | 19 | import protocol Foundation.ContiguousBytes 20 | 21 | /// Symmetric key object 22 | public struct SymmetricKey: ContiguousBytes { 23 | let bytes: [UInt8] 24 | 25 | public var bitCount: Int { 26 | return self.bytes.count * 8 27 | } 28 | 29 | public init(data: D) where D: ContiguousBytes { 30 | let bytes = data.withUnsafeBytes { buffer in 31 | return [UInt8](buffer) 32 | } 33 | self.bytes = bytes 34 | } 35 | 36 | public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { 37 | return try self.bytes.withUnsafeBytes(body) 38 | } 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /Sources/SotoCore/Credential/StaticCredential+Environment.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import SotoSignerV4 16 | 17 | public extension StaticCredential { 18 | /// construct static credentaisl from environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` 19 | /// and `AWS_SESSION_TOKEN` if it exists 20 | static func fromEnvironment() -> StaticCredential? { 21 | guard let accessKeyId = Environment["AWS_ACCESS_KEY_ID"] else { 22 | return nil 23 | } 24 | guard let secretAccessKey = Environment["AWS_SECRET_ACCESS_KEY"] else { 25 | return nil 26 | } 27 | 28 | return .init( 29 | accessKeyId: accessKeyId, 30 | secretAccessKey: secretAccessKey, 31 | sessionToken: Environment["AWS_SESSION_TOKEN"] 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/SotoCrypto/ByteArray.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // Replicating the CryptoKit framework interface for < macOS 10.15 16 | #if !os(Linux) 17 | 18 | import protocol Foundation.ContiguousBytes 19 | 20 | /// Protocol for object encapsulating an array of bytes 21 | protocol ByteArray: Sequence, ContiguousBytes, Hashable where Element == UInt8 { 22 | init(bytes: [UInt8]) 23 | var bytes: [UInt8] { get set } 24 | } 25 | 26 | extension ByteArray { 27 | public func makeIterator() -> Array.Iterator { 28 | return bytes.makeIterator() 29 | } 30 | 31 | public init?(bufferPointer: UnsafeRawBufferPointer) { 32 | self.init(bytes: [UInt8](bufferPointer)) 33 | } 34 | 35 | public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { 36 | return try bytes.withUnsafeBytes(body) 37 | } 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /scripts/generate-contributors-list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the SwiftNIO open source project 5 | ## 6 | ## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 11 | ## 12 | ## SPDX-License-Identifier: Apache-2.0 13 | ## 14 | ##===----------------------------------------------------------------------===## 15 | 16 | set -eu 17 | here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 18 | contributors=$( cd "$here"/.. && git shortlog -es | cut -f2 | sed 's/^/- /' ) 19 | 20 | cat > "$here/../CONTRIBUTORS.txt" <<- EOF 21 | For the purpose of tracking copyright, this is the list of individuals and 22 | organizations who have contributed source code to soto-core. 23 | 24 | For employees of an organization/company where the copyright of work done 25 | by employees of that company is held by the company itself, only the company 26 | needs to be listed here. 27 | 28 | ## COPYRIGHT HOLDERS 29 | 30 | ### Contributors 31 | 32 | $contributors 33 | 34 | **Updating this list** 35 | 36 | Please do not edit this file manually. It is generated using \`./scripts/generate_contributors_list.sh\`. If a name is misspelled or appearing multiple times: add an entry in \`./.mailmap\` 37 | EOF 38 | -------------------------------------------------------------------------------- /Benchmark/Sources/soto-benchmark/AWSSignerV4Suite.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Benchmark 16 | import Foundation 17 | import SotoSignerV4 18 | 19 | let awsSignerV4Suite = BenchmarkSuite(name: "AWSSignerV4", settings: Iterations(1000), WarmupIterations(2)) { suite in 20 | let string = "testing, testing, 1,2,1,2" 21 | let credentials: Credential = StaticCredential(accessKeyId: "MYACCESSKEY", secretAccessKey: "MYSECRETACCESSKEY") 22 | let signer = AWSSigner(credentials: credentials, name: "s3", region: "eu-west-1") 23 | 24 | suite.benchmark("sign-headers") { 25 | _ = signer.signHeaders(url: URL(string: "https://test-bucket.s3.amazonaws.com/test-put.txt")!, method: .GET, headers: ["Content-Type": "application/x-www-form-urlencoded; charset=utf-8"], body: .string(string)) 26 | } 27 | 28 | suite.benchmark("sign-url") { 29 | _ = signer.signURL(url: URL(string: "https://test-bucket.s3.amazonaws.com/test-put.txt")!, method: .GET, body: .string(string), expires: .hours(1)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Currently we support versions 4.x.x and 5.x.x of Soto. These will receive security updates as and when needed. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | If you believe you have found a security vulnerability in Soto please do not post this in a public forum, do not create a GitHub Issue. Instead you should email [security@soto.codes](mailto:security@soto.codes) with details of the issue. 10 | 11 | #### What happens next? 12 | 13 | * A member of the team will acknowledge receipt of the report within 5 14 | working days. This may include a request for additional 15 | information about reproducing the vulnerability. 16 | * We will privately inform the Swift Server Work Group ([SSWG][sswg]) of the 17 | vulnerability within 10 days of the report as per their [security 18 | guidelines][sswg-security]. 19 | * Once we have identified a fix we may ask you to validate it. We aim to do this 20 | within 30 days, but this may not always be possible, for example when the 21 | vulnerability is internal to Amazon Web Services(AWS). In this situation we will 22 | forward the issue to AWS. 23 | * We will decide on a planned release date and let you know when it is. 24 | * Once the fix has been released we will publish a security advisory on GitHub 25 | and the [SSWG][sswg] will announce the vulnerability on the [Swift 26 | forums][swift-forums-sec]. 27 | 28 | [sswg]: https://github.com/swift-server/sswg 29 | [sswg-security]: https://github.com/swift-server/sswg/blob/main/process/incubation.md#security-best-practices 30 | [swift-forums-sec]: https://forums.swift.org/c/server/security-updates/ 31 | -------------------------------------------------------------------------------- /Sources/SotoCore/Credential/ExpiringCredential.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import struct Foundation.Date 16 | import struct Foundation.TimeInterval 17 | import SotoSignerV4 18 | 19 | public protocol ExpiringCredential: Credential { 20 | /// Will credential expire within a certain time 21 | func isExpiring(within: TimeInterval) -> Bool 22 | } 23 | 24 | public extension ExpiringCredential { 25 | /// Has credential expired 26 | var isExpired: Bool { 27 | isExpiring(within: 0) 28 | } 29 | } 30 | 31 | /// Basic implementation of a struct conforming to ExpiringCredential to be used with the `RotatingCredentialProvider` 32 | public struct RotatingCredential: ExpiringCredential { 33 | public init(accessKeyId: String, secretAccessKey: String, sessionToken: String?, expiration: Date) { 34 | self.accessKeyId = accessKeyId 35 | self.secretAccessKey = secretAccessKey 36 | self.sessionToken = sessionToken 37 | self.expiration = expiration 38 | } 39 | 40 | /// Will credential expire within a certain time 41 | public func isExpiring(within interval: TimeInterval) -> Bool { 42 | return self.expiration.timeIntervalSinceNow < interval 43 | } 44 | 45 | public let accessKeyId: String 46 | public let secretAccessKey: String 47 | public let sessionToken: String? 48 | public let expiration: Date 49 | } 50 | -------------------------------------------------------------------------------- /scripts/expat-symbols.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Soto for AWS open source project 5 | ## 6 | ## Copyright (c) 2020 the Soto project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## See CONTRIBUTORS.txt for the list of Soto project authors 11 | ## 12 | ## SPDX-License-Identifier: Apache-2.0 13 | ## 14 | ##===----------------------------------------------------------------------===## 15 | 16 | EXPAT_PREFIX_FILE="Sources/CSotoExpat/include/expat_prefix_symbols.h" 17 | 18 | # build CSotoExpat 19 | swift build --product CSotoExpat --enable-test-discovery 20 | 21 | # get public symbols 22 | find .build/x86_64-apple-macosx/debug/CSotoExpat.build -name "*.o" -exec nm -gUj {} \; > symbols.txt 23 | 24 | cat > "$EXPAT_PREFIX_FILE" << "EOF" 25 | //===----------------------------------------------------------------------===// 26 | // 27 | // This source file is part of the Soto for AWS open source project 28 | // 29 | // Copyright (c) 2017-2020 the Soto project authors 30 | // Licensed under Apache License v2.0 31 | // 32 | // See LICENSE.txt for license information 33 | // See CONTRIBUTORS.txt for the list of Soto project authors 34 | // 35 | // SPDX-License-Identifier: Apache-2.0 36 | // 37 | //===----------------------------------------------------------------------===// 38 | 39 | #ifndef _EXPAT_PREFIX_SYMBOLS_H_ 40 | #define _EXPAT_PREFIX_SYMBOLS_H_ 41 | 42 | #define EXPAT_PREFIX Soto 43 | #define EXPAT_ADD_PREFIX(a, b) a ## _ ## b 44 | 45 | EOF 46 | 47 | for i in $( cat symbols.txt ); do 48 | SYMBOL="${i:1}" 49 | echo "#define $SYMBOL EXPAT_ADD_PREFIX(EXPAT_PREFIX, $SYMBOL)" >> "$EXPAT_PREFIX_FILE" 50 | done 51 | 52 | cat >> "$EXPAT_PREFIX_FILE" << "EOF" 53 | 54 | #endif // _EXPAT_PREFIX_SYMBOLS_H_ 55 | EOF 56 | -------------------------------------------------------------------------------- /Sources/SotoCore/Doc/AWSShape+Encoder.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import class Foundation.JSONEncoder 16 | import NIO 17 | import SotoXML 18 | 19 | internal extension AWSEncodableShape { 20 | /// Encode AWSShape as JSON 21 | func encodeAsJSON(byteBufferAllocator: ByteBufferAllocator) throws -> ByteBuffer { 22 | let encoder = JSONEncoder() 23 | encoder.dateEncodingStrategy = .secondsSince1970 24 | return try encoder.encodeAsByteBuffer(self, allocator: byteBufferAllocator) 25 | } 26 | 27 | /// Encode AWSShape as XML 28 | func encodeAsXML(rootName: String? = nil) throws -> XML.Element { 29 | let xml = try XMLEncoder().encode(self, name: rootName) 30 | if let xmlNamespace = Self._xmlNamespace { 31 | xml.addNamespace(XML.Node.namespace(stringValue: xmlNamespace)) 32 | } 33 | return xml 34 | } 35 | 36 | /// Encode AWSShape as a query array 37 | func encodeAsQuery(with keys: [String: String]) throws -> String? { 38 | var encoder = QueryEncoder() 39 | encoder.additionalKeys = keys 40 | return try encoder.encode(self) 41 | } 42 | 43 | /// Encode AWSShape as a query array 44 | func encodeAsQueryForEC2(with keys: [String: String]) throws -> String? { 45 | var encoder = QueryEncoder() 46 | encoder.additionalKeys = keys 47 | encoder.ec2 = true 48 | return try encoder.encode(self) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/SotoCrypto/MD5.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // Replicating the CryptoKit framework interface for < macOS 10.15 16 | 17 | #if !os(Linux) 18 | 19 | import CommonCrypto 20 | 21 | public extension Insecure { 22 | struct MD5Digest: ByteDigest { 23 | public static var byteCount: Int { return Int(CC_MD5_DIGEST_LENGTH) } 24 | public var bytes: [UInt8] 25 | } 26 | 27 | struct MD5: CCHashFunction { 28 | public typealias Digest = MD5Digest 29 | public static var algorithm: CCHmacAlgorithm { return CCHmacAlgorithm(kCCHmacAlgMD5) } 30 | var context: CC_MD5_CTX 31 | 32 | public static func hash(bufferPointer: UnsafeRawBufferPointer) -> Self.Digest { 33 | var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount) 34 | CC_MD5(bufferPointer.baseAddress, CC_LONG(bufferPointer.count), &digest) 35 | return .init(bytes: digest) 36 | } 37 | 38 | public init() { 39 | self.context = CC_MD5_CTX() 40 | CC_MD5_Init(&self.context) 41 | } 42 | 43 | public mutating func update(bufferPointer: UnsafeRawBufferPointer) { 44 | CC_MD5_Update(&self.context, bufferPointer.baseAddress, CC_LONG(bufferPointer.count)) 45 | } 46 | 47 | public mutating func finalize() -> Self.Digest { 48 | var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount) 49 | CC_MD5_Final(&digest, &self.context) 50 | return .init(bytes: digest) 51 | } 52 | } 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /scripts/templates/generate-region/Region-Tests.stencil: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // THIS FILE IS AUTOMATICALLY GENERATED by https://github.com/soto-project/soto-core/scripts/generate-region-tests.swift. DO NOT EDIT. 16 | 17 | import SotoCore 18 | import XCTest 19 | 20 | class RegionTests: XCTestCase { 21 | 22 | private func testStringToOneRegion(regionName: String, regionEnum: Region) { 23 | let region = Region(awsRegionName: regionName) 24 | XCTAssertNotNil(region) 25 | XCTAssert(region! == regionEnum) 26 | } 27 | 28 | func testStringToRegion() { 29 | 30 | {%for region in regions %} 31 | self.testStringToOneRegion(regionName: "{{region.name}}", regionEnum: Region.{{region.enum}}) 32 | {%endfor %} 33 | } 34 | 35 | func testStringToInvalidRegion() { 36 | XCTAssertNil(Region(awsRegionName: "xxx")) 37 | } 38 | 39 | func testRegionEnumRawValue() { 40 | let region = Region(rawValue: "my-region") 41 | if Region.other("my-region") == region { 42 | XCTAssertEqual(region.rawValue, "my-region") 43 | } else { 44 | XCTFail("Did not construct Region.other()") 45 | } 46 | } 47 | 48 | func testRegionEnumExistingRegion() { 49 | var region : Region 50 | 51 | {%for region in regions %} 52 | region = Region(rawValue: "{{region.name}}") 53 | if Region.{{region.enum}} == region { 54 | XCTAssertEqual(region.rawValue, "{{region.name}}") 55 | } else { 56 | XCTFail("Did not construct Region(rawValue:) for {{region.name}}") 57 | } 58 | 59 | {%endfor %} 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /scripts/templates/generate-errors/generate-errors.stencil: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // THIS FILE IS AUTOMATICALLY GENERATED by https://github.com/soto-project/soto-core/scripts/generate-errors.swift. DO NOT EDIT. 16 | 17 | import NIOHTTP1 18 | 19 | public struct {{name}}: AWSErrorType { 20 | enum Code: String { 21 | {%for error in errors %} 22 | case {{error.enum}} = "{{error.name}}" 23 | {%endfor %} 24 | } 25 | 26 | private let error: Code 27 | public let context: AWSErrorContext? 28 | 29 | /// initialize {{name}} 30 | public init?(errorCode: String, context: AWSErrorContext) { 31 | var errorCode = errorCode 32 | // remove "Exception" suffix 33 | if errorCode.hasSuffix("Exception") { 34 | errorCode = String(errorCode.dropLast(9)) 35 | } 36 | guard let error = Code(rawValue: errorCode) else { return nil } 37 | self.error = error 38 | self.context = context 39 | } 40 | 41 | internal init(_ error: Code, context: AWSErrorContext? = nil) { 42 | self.error = error 43 | self.context = context 44 | } 45 | 46 | /// return error code string 47 | public var errorCode: String { self.error.rawValue } 48 | 49 | {%for error in errors %} 50 | // {{error.description}} 51 | public static var {{error.enum}}: {{name}} { .init(.{{error.enum}}) } 52 | {%endfor %} 53 | } 54 | 55 | extension {{name}}: Equatable { 56 | public static func == (lhs: {{name}}, rhs: {{name}}) -> Bool { 57 | lhs.error == rhs.error 58 | } 59 | } 60 | 61 | extension {{name}}: CustomStringConvertible { 62 | public var description: String { 63 | return "\(self.error.rawValue): \(message ?? "")" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/SotoCore/Message/AWSResponse+HAL.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import class Foundation.JSONSerialization 16 | import NIO 17 | 18 | // AWS HAL services I know of are APIGateway, Pinpoint, Greengrass 19 | extension AWSResponse { 20 | /// return if body is hypertext application language 21 | var isHypertextApplicationLanguage: Bool { 22 | guard case .json = self.body, 23 | let contentType = self.headers["content-type"] as? String, 24 | contentType.contains("hal+json") 25 | else { 26 | return false 27 | } 28 | return true 29 | } 30 | 31 | /// process hal+json data. Extract properties from HAL 32 | func getHypertextApplicationLanguageDictionary() throws -> [String: Any] { 33 | guard case .json(let buffer) = self.body else { return [:] } 34 | // extract embedded resources from HAL 35 | guard let data = buffer.getData(at: buffer.readerIndex, length: buffer.readableBytes) else { return [:] } 36 | let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) 37 | guard var dictionary = jsonObject as? [String: Any] else { return [:] } 38 | guard let embedded = dictionary["_embedded"], 39 | let embeddedDictionary = embedded as? [String: Any] 40 | else { 41 | return dictionary 42 | } 43 | 44 | // remove _links and _embedded elements of dictionary to reduce the size of the new dictionary 45 | dictionary["_links"] = nil 46 | dictionary["_embedded"] = nil 47 | // merge embedded resources into original dictionary 48 | dictionary.merge(embeddedDictionary) { first, _ in return first } 49 | return dictionary 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/winconfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __ _ 3 | ___\ \/ /_ __ __ _| |_ 4 | / _ \\ /| '_ \ / _` | __| 5 | | __// \| |_) | (_| | |_ 6 | \___/_/\_\ .__/ \__,_|\__| 7 | |_| XML parser 8 | 9 | Copyright (c) 1997-2000 Thai Open Source Software Center Ltd 10 | Copyright (c) 2000-2017 Expat development team 11 | Licensed under the MIT license: 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining 14 | a copy of this software and associated documentation files (the 15 | "Software"), to deal in the Software without restriction, including 16 | without limitation the rights to use, copy, modify, merge, publish, 17 | distribute, sublicense, and/or sell copies of the Software, and to permit 18 | persons to whom the Software is furnished to do so, subject to the 19 | following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included 22 | in all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 27 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 28 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 29 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 30 | USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | #ifndef WINCONFIG_H 34 | #define WINCONFIG_H 35 | 36 | #define WIN32_LEAN_AND_MEAN 37 | #include 38 | #undef WIN32_LEAN_AND_MEAN 39 | 40 | #include 41 | #include 42 | 43 | #if defined(HAVE_EXPAT_CONFIG_H) /* e.g. MinGW */ 44 | # include 45 | #else /* !defined(HAVE_EXPAT_CONFIG_H) */ 46 | 47 | # define XML_NS 1 48 | # define XML_DTD 1 49 | # define XML_CONTEXT_BYTES 1024 50 | 51 | /* we will assume all Windows platforms are little endian */ 52 | # define BYTEORDER 1234 53 | 54 | #endif /* !defined(HAVE_EXPAT_CONFIG_H) */ 55 | 56 | #endif /* ndef WINCONFIG_H */ 57 | -------------------------------------------------------------------------------- /scripts/generate-region-test.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env swift sh 2 | //===----------------------------------------------------------------------===// 3 | // 4 | // This source file is part of the Soto for AWS open source project 5 | // 6 | // Copyright (c) 2017-2020 the Soto project authors 7 | // Licensed under Apache License v2.0 8 | // 9 | // See LICENSE.txt for license information 10 | // See CONTRIBUTORS.txt for the list of Soto project authors 11 | // 12 | // SPDX-License-Identifier: Apache-2.0 13 | // 14 | //===----------------------------------------------------------------------===// 15 | 16 | import Foundation 17 | import SotoCore // soto-project/soto-core ~> 5.0.0-beta.3.0 18 | import SotoSSM // soto-project/soto ~> 5.0.0-beta.3.0 19 | import Stencil // soto-project/Stencil 20 | 21 | let REGION_PATH = "/aws/service/global-infrastructure/regions" 22 | 23 | print("Loading Region List") 24 | struct RegionDesc { 25 | let `enum`: String 26 | let name: String 27 | } 28 | 29 | var regionDescs: [RegionDesc] = [] 30 | 31 | let client = AWSClient( 32 | httpClientProvider: .createNew 33 | ) 34 | let ssm = SSM(client: client, region: Region.euwest1) 35 | let request = SSM.GetParametersByPathRequest( 36 | path: REGION_PATH 37 | ) 38 | do { 39 | var result = try ssm.getParametersByPath(request).wait() 40 | while result.nextToken != nil { 41 | for p in result.parameters! { 42 | regionDescs.append( 43 | RegionDesc(enum: p.value!.filter { return $0.isLetter || $0.isNumber }, name: p.value!) 44 | ) 45 | } 46 | let request = SSM.GetParametersByPathRequest( 47 | nextToken: result.nextToken, 48 | path: REGION_PATH 49 | ) 50 | result = try ssm.getParametersByPath(request).wait() 51 | } 52 | } catch (let error) { 53 | print("Failed with \(error)") 54 | } 55 | 56 | print("Loading templates") 57 | let fsLoader = FileSystemLoader(paths: ["./scripts/templates/generate-region"]) 58 | let environment = Environment(loader: fsLoader) 59 | 60 | print("Creating RegionTests.swift") 61 | 62 | let context: [String: Any] = [ 63 | "regions": regionDescs.sorted { $0.name < $1.name }, 64 | ] 65 | 66 | let regionsFile = try environment.renderTemplate(name: "Region-Tests.stencil", context: context) 67 | try Data(regionsFile.utf8).write(to: URL(fileURLWithPath: "Tests/SotoCoreTests/Doc/RegionTests.swift")) 68 | 69 | print("Done") 70 | -------------------------------------------------------------------------------- /Tests/INIParserTests/INIParserTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | @testable import INIParser 16 | import XCTest 17 | 18 | class INIParserTests: XCTestCase { 19 | func testExample() { 20 | let raw = """ 21 | ; last modified 1 April 2017 by Rockford Wei 22 | ## This is another comment 23 | freeVar1 = 1 24 | freeVar2 = 2; 25 | url = http://example.com/results?limit=10 26 | [owner] 27 | name = Rocky 28 | organization = PerfectlySoft 29 | ; 30 | [database] 31 | server = 192.0.2.42 ; use IP address in case network name resolution is not working 32 | port = 143 33 | file = \"中文.dat ' ' \" 34 | [汉化] 35 | 变量1 = 🇨🇳 ;使用utf8 36 | 变量2 = 加拿大。 37 | [ 乱死了 ] 38 | foo = bar 39 | """ 40 | 41 | var ini: INIParser? 42 | XCTAssertNoThrow(ini = try INIParser(raw)) 43 | 44 | XCTAssertEqual(ini?.anonymousSection["freeVar1"] ?? "", "1") 45 | XCTAssertEqual(ini?.anonymousSection["freeVar2"] ?? "", "2") 46 | XCTAssertEqual(ini?.anonymousSection["url"] ?? "", "http://example.com/results?limit=10") 47 | XCTAssertEqual(ini?.sections["owner"]?["name"] ?? "", "Rocky") 48 | XCTAssertEqual(ini?.sections["owner"]?["organization"] ?? "", "PerfectlySoft") 49 | XCTAssertEqual(ini?.sections["database"]?["server"] ?? "", "192.0.2.42") 50 | XCTAssertEqual(ini?.sections["database"]?["port"] ?? "", "143") 51 | XCTAssertEqual(ini?.sections["database"]?["file"] ?? "", "\"中文.dat \' \' \"") 52 | XCTAssertEqual(ini?.sections["汉化"]?["变量1"] ?? "", "🇨🇳") 53 | XCTAssertEqual(ini?.sections["汉化"]?["变量2"] ?? "", "加拿大。") 54 | XCTAssertNotNil(ini?.sections[" 乱死了 "]) 55 | } 56 | 57 | static var allTests = [ 58 | ("testExample", testExample), 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | release: 11 | types: [published] 12 | 13 | env: 14 | ENABLE_TIMING_TESTS: "false" 15 | AWS_LOG_LEVEL: "trace" 16 | 17 | jobs: 18 | macos: 19 | runs-on: macOS-latest 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v1 23 | with: 24 | fetch-depth: 1 25 | - name: SPM tests 26 | run: swift test --enable-code-coverage --sanitize=thread 27 | - name: Convert coverage files 28 | run: | 29 | xcrun llvm-cov export -format "lcov" \ 30 | .build/debug/soto-corePackageTests.xctest/Contents/MacOs/soto-corePackageTests \ 31 | -ignore-filename-regex="\/Tests\/" \ 32 | -instr-profile=.build/debug/codecov/default.profdata > info.lcov 33 | - name: Upload to codecov.io 34 | uses: codecov/codecov-action@v1 35 | with: 36 | file: info.lcov 37 | 38 | ios: 39 | runs-on: macOS-latest 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v1 43 | with: 44 | fetch-depth: 1 45 | - name: Xcodebuild 46 | run: | 47 | xcodebuild -scheme soto-core-Package -quiet -destination 'platform=iOS Simulator,name=iPhone 11' 48 | xcodebuild test -scheme soto-core-Package -destination 'platform=iOS Simulator,name=iPhone 11' 49 | 50 | linux: 51 | runs-on: ubuntu-latest 52 | strategy: 53 | matrix: 54 | tag: 55 | - swift:5.1 56 | - swift:5.3 57 | - swift:5.4 58 | container: 59 | image: ${{ matrix.tag }} 60 | steps: 61 | - name: Checkout 62 | uses: actions/checkout@v1 63 | with: 64 | fetch-depth: 1 65 | - name: Install dependencies 66 | run: | 67 | apt-get update -qq 68 | apt-get install -q -y tzdata zlib1g-dev curl 69 | - name: Test 70 | run: swift test --enable-test-discovery --enable-code-coverage --sanitize=thread 71 | - name: Convert coverage files 72 | run: | 73 | llvm-cov export -format="lcov" \ 74 | .build/debug/soto-corePackageTests.xctest \ 75 | -ignore-filename-regex="\/Tests\/" \ 76 | -instr-profile .build/debug/codecov/default.profdata > info.lcov 77 | - name: Upload to codecov.io 78 | uses: codecov/codecov-action@v1 79 | with: 80 | file: info.lcov 81 | -------------------------------------------------------------------------------- /Sources/SotoCore/Errors/ServerErrors.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // THIS FILE IS AUTOMATICALLY GENERATED by https://github.com/soto-project/soto-core/scripts/generate-errors.swift. DO NOT EDIT. 16 | 17 | import NIOHTTP1 18 | 19 | public struct AWSServerError: AWSErrorType { 20 | enum Code: String { 21 | case internalFailure = "InternalFailure" 22 | case serviceUnavailable = "ServiceUnavailable" 23 | } 24 | 25 | private let error: Code 26 | public let context: AWSErrorContext? 27 | 28 | /// initialize AWSServerError 29 | public init?(errorCode: String, context: AWSErrorContext) { 30 | var errorCode = errorCode 31 | // remove "Exception" suffix 32 | if errorCode.hasSuffix("Exception") { 33 | errorCode = String(errorCode.dropLast(9)) 34 | } 35 | guard let error = Code(rawValue: errorCode) else { return nil } 36 | self.error = error 37 | self.context = context 38 | } 39 | 40 | internal init(_ error: Code, context: AWSErrorContext? = nil) { 41 | self.error = error 42 | self.context = context 43 | } 44 | 45 | /// return error code string 46 | public var errorCode: String { self.error.rawValue } 47 | 48 | // The request processing has failed because of an unknown error, exception or failure. 49 | public static var internalFailure: AWSServerError { .init(.internalFailure) } 50 | // The request has failed due to a temporary failure of the server. 51 | public static var serviceUnavailable: AWSServerError { .init(.serviceUnavailable) } 52 | } 53 | 54 | extension AWSServerError: Equatable { 55 | public static func == (lhs: AWSServerError, rhs: AWSServerError) -> Bool { 56 | lhs.error == rhs.error 57 | } 58 | } 59 | 60 | extension AWSServerError: CustomStringConvertible { 61 | public var description: String { 62 | return "\(self.error.rawValue): \(message ?? "")" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/libexpat.def: -------------------------------------------------------------------------------- 1 | ; DEF file for MS VC++ 2 | 3 | LIBRARY 4 | EXPORTS 5 | XML_DefaultCurrent @1 6 | XML_ErrorString @2 7 | XML_ExpatVersion @3 8 | XML_ExpatVersionInfo @4 9 | XML_ExternalEntityParserCreate @5 10 | XML_GetBase @6 11 | XML_GetBuffer @7 12 | XML_GetCurrentByteCount @8 13 | XML_GetCurrentByteIndex @9 14 | XML_GetCurrentColumnNumber @10 15 | XML_GetCurrentLineNumber @11 16 | XML_GetErrorCode @12 17 | XML_GetIdAttributeIndex @13 18 | XML_GetInputContext @14 19 | XML_GetSpecifiedAttributeCount @15 20 | XML_Parse @16 21 | XML_ParseBuffer @17 22 | XML_ParserCreate @18 23 | XML_ParserCreateNS @19 24 | XML_ParserCreate_MM @20 25 | XML_ParserFree @21 26 | XML_SetAttlistDeclHandler @22 27 | XML_SetBase @23 28 | XML_SetCdataSectionHandler @24 29 | XML_SetCharacterDataHandler @25 30 | XML_SetCommentHandler @26 31 | XML_SetDefaultHandler @27 32 | XML_SetDefaultHandlerExpand @28 33 | XML_SetDoctypeDeclHandler @29 34 | XML_SetElementDeclHandler @30 35 | XML_SetElementHandler @31 36 | XML_SetEncoding @32 37 | XML_SetEndCdataSectionHandler @33 38 | XML_SetEndDoctypeDeclHandler @34 39 | XML_SetEndElementHandler @35 40 | XML_SetEndNamespaceDeclHandler @36 41 | XML_SetEntityDeclHandler @37 42 | XML_SetExternalEntityRefHandler @38 43 | XML_SetExternalEntityRefHandlerArg @39 44 | XML_SetNamespaceDeclHandler @40 45 | XML_SetNotStandaloneHandler @41 46 | XML_SetNotationDeclHandler @42 47 | XML_SetParamEntityParsing @43 48 | XML_SetProcessingInstructionHandler @44 49 | XML_SetReturnNSTriplet @45 50 | XML_SetStartCdataSectionHandler @46 51 | XML_SetStartDoctypeDeclHandler @47 52 | XML_SetStartElementHandler @48 53 | XML_SetStartNamespaceDeclHandler @49 54 | XML_SetUnknownEncodingHandler @50 55 | XML_SetUnparsedEntityDeclHandler @51 56 | XML_SetUserData @52 57 | XML_SetXmlDeclHandler @53 58 | XML_UseParserAsHandlerArg @54 59 | ; added with version 1.95.3 60 | XML_ParserReset @55 61 | XML_SetSkippedEntityHandler @56 62 | ; added with version 1.95.5 63 | XML_GetFeatureList @57 64 | XML_UseForeignDTD @58 65 | ; added with version 1.95.6 66 | XML_FreeContentModel @59 67 | XML_MemMalloc @60 68 | XML_MemRealloc @61 69 | XML_MemFree @62 70 | ; added with version 1.95.8 71 | XML_StopParser @63 72 | XML_ResumeParser @64 73 | XML_GetParsingStatus @65 74 | ; added with version 2.1.1 75 | ; XML_GetAttributeInfo @66 76 | XML_SetHashSalt @67 77 | ; added with version 2.2.5 78 | _INTERNAL_trim_to_complete_utf8_characters @68 79 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/libexpatw.def: -------------------------------------------------------------------------------- 1 | ; DEF file for MS VC++ 2 | 3 | LIBRARY 4 | EXPORTS 5 | XML_DefaultCurrent @1 6 | XML_ErrorString @2 7 | XML_ExpatVersion @3 8 | XML_ExpatVersionInfo @4 9 | XML_ExternalEntityParserCreate @5 10 | XML_GetBase @6 11 | XML_GetBuffer @7 12 | XML_GetCurrentByteCount @8 13 | XML_GetCurrentByteIndex @9 14 | XML_GetCurrentColumnNumber @10 15 | XML_GetCurrentLineNumber @11 16 | XML_GetErrorCode @12 17 | XML_GetIdAttributeIndex @13 18 | XML_GetInputContext @14 19 | XML_GetSpecifiedAttributeCount @15 20 | XML_Parse @16 21 | XML_ParseBuffer @17 22 | XML_ParserCreate @18 23 | XML_ParserCreateNS @19 24 | XML_ParserCreate_MM @20 25 | XML_ParserFree @21 26 | XML_SetAttlistDeclHandler @22 27 | XML_SetBase @23 28 | XML_SetCdataSectionHandler @24 29 | XML_SetCharacterDataHandler @25 30 | XML_SetCommentHandler @26 31 | XML_SetDefaultHandler @27 32 | XML_SetDefaultHandlerExpand @28 33 | XML_SetDoctypeDeclHandler @29 34 | XML_SetElementDeclHandler @30 35 | XML_SetElementHandler @31 36 | XML_SetEncoding @32 37 | XML_SetEndCdataSectionHandler @33 38 | XML_SetEndDoctypeDeclHandler @34 39 | XML_SetEndElementHandler @35 40 | XML_SetEndNamespaceDeclHandler @36 41 | XML_SetEntityDeclHandler @37 42 | XML_SetExternalEntityRefHandler @38 43 | XML_SetExternalEntityRefHandlerArg @39 44 | XML_SetNamespaceDeclHandler @40 45 | XML_SetNotStandaloneHandler @41 46 | XML_SetNotationDeclHandler @42 47 | XML_SetParamEntityParsing @43 48 | XML_SetProcessingInstructionHandler @44 49 | XML_SetReturnNSTriplet @45 50 | XML_SetStartCdataSectionHandler @46 51 | XML_SetStartDoctypeDeclHandler @47 52 | XML_SetStartElementHandler @48 53 | XML_SetStartNamespaceDeclHandler @49 54 | XML_SetUnknownEncodingHandler @50 55 | XML_SetUnparsedEntityDeclHandler @51 56 | XML_SetUserData @52 57 | XML_SetXmlDeclHandler @53 58 | XML_UseParserAsHandlerArg @54 59 | ; added with version 1.95.3 60 | XML_ParserReset @55 61 | XML_SetSkippedEntityHandler @56 62 | ; added with version 1.95.5 63 | XML_GetFeatureList @57 64 | XML_UseForeignDTD @58 65 | ; added with version 1.95.6 66 | XML_FreeContentModel @59 67 | XML_MemMalloc @60 68 | XML_MemRealloc @61 69 | XML_MemFree @62 70 | ; added with version 1.95.8 71 | XML_StopParser @63 72 | XML_ResumeParser @64 73 | XML_GetParsingStatus @65 74 | ; added with version 2.1.1 75 | ; XML_GetAttributeInfo @66 76 | XML_SetHashSalt @67 77 | ; added with version 2.2.5 78 | _INTERNAL_trim_to_complete_utf8_characters @68 79 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # __ __ _ 3 | # ___\ \/ /_ __ __ _| |_ 4 | # / _ \\ /| '_ \ / _` | __| 5 | # | __// \| |_) | (_| | |_ 6 | # \___/_/\_\ .__/ \__,_|\__| 7 | # |_| XML parser 8 | # 9 | # Copyright (c) 2017 Expat development team 10 | # Licensed under the MIT license: 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining 13 | # a copy of this software and associated documentation files (the 14 | # "Software"), to deal in the Software without restriction, including 15 | # without limitation the rights to use, copy, modify, merge, publish, 16 | # distribute, sublicense, and/or sell copies of the Software, and to permit 17 | # persons to whom the Software is furnished to do so, subject to the 18 | # following conditions: 19 | # 20 | # The above copyright notice and this permission notice shall be included 21 | # in all copies or substantial portions of the Software. 22 | # 23 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 26 | # NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 27 | # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 28 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 29 | # USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | include_HEADERS = \ 32 | ../expat_config.h \ 33 | expat.h \ 34 | expat_external.h 35 | 36 | lib_LTLIBRARIES = libexpat.la 37 | 38 | libexpat_la_LDFLAGS = \ 39 | @AM_LDFLAGS@ \ 40 | -no-undefined \ 41 | -version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@ 42 | 43 | libexpat_la_SOURCES = \ 44 | xmlparse.c \ 45 | xmltok.c \ 46 | xmlrole.c 47 | 48 | doc_DATA = \ 49 | ../AUTHORS \ 50 | ../Changes 51 | 52 | install-data-hook: 53 | cd "$(DESTDIR)$(docdir)" && $(am__mv) Changes changelog 54 | 55 | uninstall-local: 56 | $(RM) "$(DESTDIR)$(docdir)/changelog" 57 | 58 | EXTRA_DIST = \ 59 | ascii.h \ 60 | asciitab.h \ 61 | expat_external.h \ 62 | expat.h \ 63 | iasciitab.h \ 64 | internal.h \ 65 | latin1tab.h \ 66 | libexpat.def \ 67 | libexpatw.def \ 68 | nametab.h \ 69 | siphash.h \ 70 | utf8tab.h \ 71 | winconfig.h \ 72 | xmlrole.h \ 73 | xmltok.h \ 74 | xmltok_impl.c \ 75 | xmltok_impl.h \ 76 | xmltok_ns.c 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Legal 4 | By submitting a pull request, you represent that you have the right to license your contribution to the community, and agree by submitting the patch 5 | that your contributions are licensed under the Apache 2.0 license (see [LICENSE](LICENSE.txt)). 6 | 7 | ## Contributor Conduct 8 | All contributors are expected to adhere to the project's [Code of Conduct](CODE_OF_CONDUCT.md). 9 | 10 | ## Submitting a bug or issue 11 | Please ensure to include the following in your bug report 12 | - A consise description of the issue, what happened and what you expected. 13 | - Simple reproduction steps 14 | - Version of the library you are using 15 | - Contextual information (Swift version, OS etc) 16 | 17 | ## Submitting a Pull Request 18 | 19 | Please ensure to include the following in your Pull Request 20 | - A description of what you are trying to do. What the PR provides to the library, additional functionality, fixing a bug etc 21 | - A description of the code changes 22 | - Documentation on how these changes are being tested 23 | - Additional tests to show your code working and to ensure future changes don't break your code. 24 | 25 | Please keep you PRs to a minimal number of changes. If a PR is large try to split it up into smaller PRs. Don't move code around unnecessarily it makes comparing old with new very hard. 26 | 27 | The main development branch of the repository is `main`. Each major version release has it's own branch named "version number".x.x eg `4.x.x` . If you are submitting code for an older version then you should use the version branch as the base for your code changes. 28 | 29 | ### Formatting 30 | 31 | We use Nick Lockwood's SwiftFormat for formatting code. PRs will not be accepted if they haven't be formatted. The current version of SwiftFormat we are using is v0.47.13. 32 | 33 | All new files need to include the following file header at the top 34 | ```swift 35 | //===----------------------------------------------------------------------===// 36 | // 37 | // This source file is part of the Soto for AWS open source project 38 | // 39 | // Copyright (c) 2017-2020 the Soto project authors 40 | // Licensed under Apache License v2.0 41 | // 42 | // See LICENSE.txt for license information 43 | // See CONTRIBUTORS.txt for the list of Soto project authors 44 | // 45 | // SPDX-License-Identifier: Apache-2.0 46 | // 47 | //===----------------------------------------------------------------------===// 48 | ``` 49 | Please ensure the dates are correct in the header. 50 | 51 | ## Community 52 | 53 | You can also contribute by becoming an active member of the Soto community. Join us on the soto-aws [slack](https://join.slack.com/t/soto-project/shared_invite/zt-juqk6l9w-z9zruW5pjlod4AscdWlz7Q). 54 | -------------------------------------------------------------------------------- /Sources/SotoCore/HTTP/AWSHTTPClient.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Foundation 16 | import Logging 17 | import NIO 18 | import NIOHTTP1 19 | 20 | /// HTTP Request 21 | public struct AWSHTTPRequest { 22 | public let url: URL 23 | public let method: HTTPMethod 24 | public let headers: HTTPHeaders 25 | public let body: AWSPayload 26 | 27 | public init(url: URL, method: HTTPMethod, headers: HTTPHeaders = [:], body: AWSPayload = .empty) { 28 | self.url = url 29 | self.method = method 30 | self.headers = headers 31 | self.body = body 32 | } 33 | } 34 | 35 | /// HTTP Response 36 | public protocol AWSHTTPResponse { 37 | /// HTTP response status 38 | var status: HTTPResponseStatus { get } 39 | /// HTTP response headers 40 | var headers: HTTPHeaders { get } 41 | /// Payload of response 42 | var body: ByteBuffer? { get } 43 | } 44 | 45 | /// Protocol defining requirements for a HTTPClient 46 | public protocol AWSHTTPClient { 47 | /// Function that streamed response chunks are sent ot 48 | typealias ResponseStream = (ByteBuffer, EventLoop) -> EventLoopFuture 49 | 50 | /// Execute HTTP request and return a future holding a HTTP Response 51 | func execute(request: AWSHTTPRequest, timeout: TimeAmount, on eventLoop: EventLoop, logger: Logger) -> EventLoopFuture 52 | 53 | /// Execute an HTTP request with a streamed response 54 | func execute(request: AWSHTTPRequest, timeout: TimeAmount, on eventLoop: EventLoop, logger: Logger, stream: @escaping ResponseStream) -> EventLoopFuture 55 | 56 | /// This should be called before an HTTP Client can be de-initialised 57 | func shutdown(queue: DispatchQueue, _ callback: @escaping (Error?) -> Void) 58 | 59 | /// Event loop group used by client 60 | var eventLoopGroup: EventLoopGroup { get } 61 | } 62 | 63 | extension AWSHTTPClient { 64 | /// Execute an HTTP request with a streamed response 65 | public func execute(request: AWSHTTPRequest, timeout: TimeAmount, on eventLoop: EventLoop, logger: Logger, stream: @escaping ResponseStream) -> EventLoopFuture { 66 | preconditionFailure("\(type(of: self)) does not support response streaming") 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/SotoCore/Credential/CredentialProviderSelector.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Logging 16 | import NIO 17 | import NIOConcurrencyHelpers 18 | import SotoSignerV4 19 | 20 | /// Protocol for CredentialProvider that uses an internal CredentialProvider 21 | /// 22 | /// When conforming to this protocol once you ahve the internal provider it should be supplying to 23 | /// the startupPromise and you should set `internalProvider` when the setupPromise 24 | /// result is available. 25 | /// ``` 26 | /// init(providers: [CredentialProviderFactory], context: CredentialProviderFactory.Context) { 27 | /// self.startupPromise = context.eventLoop.makePromise(of: CredentialProvider.self) 28 | /// self.startupPromise.futureResult.whenSuccess { result in 29 | /// self.internalProvider = result 30 | /// } 31 | /// self.setupInternalProvider(providers: providers, context: context) 32 | /// } 33 | /// ``` 34 | protocol CredentialProviderSelector: CredentialProvider, AnyObject { 35 | /// promise to find a credential provider 36 | var startupPromise: EventLoopPromise { get } 37 | var lock: Lock { get } 38 | var _internalProvider: CredentialProvider? { get set } 39 | } 40 | 41 | extension CredentialProviderSelector { 42 | /// the provider chosen to supply credentials 43 | var internalProvider: CredentialProvider? { 44 | get { 45 | self.lock.withLock { 46 | _internalProvider 47 | } 48 | } 49 | set { 50 | self.lock.withLock { 51 | _internalProvider = newValue 52 | } 53 | } 54 | } 55 | 56 | func shutdown(on eventLoop: EventLoop) -> EventLoopFuture { 57 | return self.startupPromise.futureResult.flatMap { provider in 58 | provider.shutdown(on: eventLoop) 59 | }.hop(to: eventLoop) 60 | } 61 | 62 | func getCredential(on eventLoop: EventLoop, logger: Logger) -> EventLoopFuture { 63 | if let provider = internalProvider { 64 | return provider.getCredential(on: eventLoop, logger: logger) 65 | } 66 | 67 | return self.startupPromise.futureResult.hop(to: eventLoop).flatMap { provider in 68 | return provider.getCredential(on: eventLoop, logger: logger) 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All developers should feel welcome and encouraged to contribute to Soto. Because of this we have adopted the code of conduct defined by [contributor-covenant.org](https://www.contributor-covenant.org). This document is used across many open source 4 | communities, and we think it articulates our values well. The full text is copied below: 5 | 6 | ## Contributor Code of Conduct v1.3 7 | 8 | As contributors and maintainers of this project, and in the interest of 9 | fostering an open and welcoming community, we pledge to respect all people who 10 | contribute through reporting issues, posting feature requests, updating 11 | documentation, submitting pull requests or patches, and other activities. 12 | 13 | We are committed to making participation in this project a harassment-free 14 | experience for everyone, regardless of level of experience, gender, gender 15 | identity and expression, sexual orientation, disability, personal appearance, 16 | body size, race, ethnicity, age, religion, or nationality. 17 | 18 | Examples of unacceptable behavior by participants include: 19 | 20 | * The use of sexualized language or imagery 21 | * Personal attacks 22 | * Trolling or insulting/derogatory comments 23 | * Public or private harassment 24 | * Publishing other's private information, such as physical or electronic 25 | addresses, without explicit permission 26 | * Other unethical or unprofessional conduct 27 | 28 | Project maintainers have the right and responsibility to remove, edit, or 29 | reject comments, commits, code, wiki edits, issues, and other contributions 30 | that are not aligned to this Code of Conduct, or to ban temporarily or 31 | permanently any contributor for other behaviors that they deem inappropriate, 32 | threatening, offensive, or harmful. 33 | 34 | By adopting this Code of Conduct, project maintainers commit themselves to 35 | fairly and consistently applying these principles to every aspect of managing 36 | this project. Project maintainers who do not follow or enforce the Code of 37 | Conduct may be permanently removed from the project team. 38 | 39 | This Code of Conduct applies both within project spaces and in public spaces 40 | when an individual is representing the project or its community. 41 | 42 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 43 | reported by contacting a project maintainer at [INSERT EMAIL ADDRESS]. All 44 | complaints will be reviewed and investigated and will result in a response that 45 | is deemed necessary and appropriate to the circumstances. Maintainers are 46 | obligated to maintain confidentiality with regard to the reporter of an 47 | incident. 48 | 49 | 50 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 51 | version 1.3.0, available at https://www.contributor-covenant.org/version/1/3/0/code-of-conduct.html 52 | 53 | [homepage]: https://www.contributor-covenant.org 54 | 55 | -------------------------------------------------------------------------------- /Sources/SotoCore/Credential/RuntimeSelectorCredentialProvider.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Logging 16 | import NIO 17 | import NIOConcurrencyHelpers 18 | import SotoSignerV4 19 | 20 | /// get credentials from a list of possible credential providers. Goes through list of providers from start to end 21 | /// attempting to get credentials. Once it finds a `CredentialProvider` that supplies credentials use that 22 | /// one 23 | class RuntimeSelectorCredentialProvider: CredentialProviderSelector { 24 | /// promise to find a credential provider 25 | let startupPromise: EventLoopPromise 26 | let lock = Lock() 27 | var _internalProvider: CredentialProvider? 28 | 29 | init(providers: [CredentialProviderFactory], context: CredentialProviderFactory.Context) { 30 | self.startupPromise = context.eventLoop.makePromise(of: CredentialProvider.self) 31 | self.startupPromise.futureResult.whenSuccess { result in 32 | self.internalProvider = result 33 | } 34 | self.setupInternalProvider(providers: providers, context: context) 35 | } 36 | 37 | /// goes through list of providers. If provider is able to provide credentials then use that one, otherwise move onto the next 38 | /// provider in the list 39 | private func setupInternalProvider(providers: [CredentialProviderFactory], context: CredentialProviderFactory.Context) { 40 | func _setupInternalProvider(_ index: Int) { 41 | guard index < providers.count else { 42 | self.startupPromise.fail(CredentialProviderError.noProvider) 43 | return 44 | } 45 | let providerFactory = providers[index] 46 | let provider = providerFactory.createProvider(context: context) 47 | provider.getCredential(on: context.eventLoop, logger: context.logger).whenComplete { result in 48 | switch result { 49 | case .success: 50 | context.logger.debug("Select credential provider", metadata: ["aws-credential-provider": .string("\(provider)")]) 51 | self.startupPromise.succeed(provider) 52 | case .failure: 53 | context.logger.log(level: context.options.errorLogLevel, "Select credential provider failed") 54 | _setupInternalProvider(index + 1) 55 | } 56 | } 57 | } 58 | 59 | _setupInternalProvider(0) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/SotoCrypto/HMAC.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // Replicating the CryptoKit framework interface for < macOS 10.15 16 | 17 | #if !os(Linux) 18 | 19 | import CommonCrypto 20 | import protocol Foundation.DataProtocol 21 | 22 | /// Hash Authentication Code returned by HMAC 23 | public struct HashAuthenticationCode: ByteArray { 24 | public var bytes: [UInt8] 25 | } 26 | 27 | /// Object generating HMAC for data block given a symmetric key 28 | public struct HMAC { 29 | let key: SymmetricKey 30 | var context: CCHmacContext 31 | 32 | /// return authentication code for data block given a symmetric key 33 | public static func authenticationCode(for data: D, using key: SymmetricKey) -> HashAuthenticationCode { 34 | var hmac = HMAC(key: key) 35 | hmac.update(data: data) 36 | return hmac.finalize() 37 | } 38 | 39 | /// update HMAC calculation with a block of data 40 | public mutating func update(data: D) { 41 | if let digest = data.withContiguousStorageIfAvailable({ bytes in 42 | return self.update(bufferPointer: .init(bytes)) 43 | }) { 44 | return digest 45 | } else { 46 | let buffer = UnsafeMutableBufferPointer.allocate(capacity: data.count) 47 | data.copyBytes(to: buffer) 48 | defer { buffer.deallocate() } 49 | self.update(bufferPointer: .init(buffer)) 50 | } 51 | } 52 | } 53 | 54 | extension HMAC { 55 | /// initialize HMAC with symmetric key 56 | public init(key: SymmetricKey) { 57 | self.key = key 58 | self.context = CCHmacContext() 59 | self.initialize() 60 | } 61 | 62 | /// initialize HMAC calculation 63 | mutating func initialize() { 64 | CCHmacInit(&self.context, H.algorithm, self.key.bytes, self.key.bytes.count) 65 | } 66 | 67 | /// update HMAC calculation with a buffer 68 | public mutating func update(bufferPointer: UnsafeRawBufferPointer) { 69 | CCHmacUpdate(&self.context, bufferPointer.baseAddress, bufferPointer.count) 70 | } 71 | 72 | /// finalize HMAC calculation and return authentication code 73 | public mutating func finalize() -> HashAuthenticationCode { 74 | var authenticationCode: [UInt8] = .init(repeating: 0, count: H.Digest.byteCount) 75 | CCHmacFinal(&self.context, &authenticationCode) 76 | return .init(bytes: authenticationCode) 77 | } 78 | } 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /Sources/SotoCrypto/HashFunction.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // Replicating the CryptoKit framework interface for < macOS 10.15 16 | 17 | #if !os(Linux) 18 | 19 | import CommonCrypto 20 | import protocol Foundation.DataProtocol 21 | 22 | /// Protocol for Hashing function 23 | public protocol HashFunction { 24 | /// associated digest object 25 | associatedtype Digest: SotoCrypto.Digest 26 | 27 | /// hash raw buffer 28 | static func hash(bufferPointer: UnsafeRawBufferPointer) -> Self.Digest 29 | 30 | /// initialization 31 | init() 32 | 33 | /// update hash function with data 34 | mutating func update(bufferPointer: UnsafeRawBufferPointer) 35 | /// finalize hash function and return digest 36 | mutating func finalize() -> Self.Digest 37 | } 38 | 39 | extension HashFunction { 40 | /// default version of hash which call init, update and finalize 41 | public static func hash(bufferPointer: UnsafeRawBufferPointer) -> Self.Digest { 42 | var function = Self() 43 | function.update(bufferPointer: bufferPointer) 44 | return function.finalize() 45 | } 46 | 47 | /// version of hash that takes data in any form that complies with DataProtocol 48 | public static func hash(data: D) -> Self.Digest { 49 | if let digest = data.withContiguousStorageIfAvailable({ bytes in 50 | return self.hash(bufferPointer: .init(bytes)) 51 | }) { 52 | return digest 53 | } else { 54 | let buffer = UnsafeMutableBufferPointer.allocate(capacity: data.count) 55 | data.copyBytes(to: buffer) 56 | defer { buffer.deallocate() } 57 | return self.hash(bufferPointer: .init(buffer)) 58 | } 59 | } 60 | 61 | /// version of update that takes data in any form that complies with DataProtocol 62 | public mutating func update(data: D) { 63 | if let digest = data.withContiguousStorageIfAvailable({ bytes in 64 | return self.update(bufferPointer: .init(bytes)) 65 | }) { 66 | return digest 67 | } else { 68 | let buffer = UnsafeMutableBufferPointer.allocate(capacity: data.count) 69 | data.copyBytes(to: buffer) 70 | defer { buffer.deallocate() } 71 | self.update(bufferPointer: .init(buffer)) 72 | } 73 | } 74 | } 75 | 76 | /// public protocol for Common Crypto hash functions 77 | public protocol CCHashFunction: HashFunction { 78 | static var algorithm: CCHmacAlgorithm { get } 79 | } 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /Sources/SotoCore/Errors/Error.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import NIOHTTP1 16 | 17 | /// Standard Error type returned by Soto. Initialized with error code and message. Must provide an implementation of var description : String 18 | public protocol AWSErrorType: Error, CustomStringConvertible { 19 | /// initialize error 20 | init?(errorCode: String, context: AWSErrorContext) 21 | /// Error code return by AWS 22 | var errorCode: String { get } 23 | /// additional context information related to the error 24 | var context: AWSErrorContext? { get } 25 | } 26 | 27 | extension AWSErrorType { 28 | public var localizedDescription: String { 29 | return description 30 | } 31 | 32 | public var message: String? { 33 | return context?.message 34 | } 35 | } 36 | 37 | /// Additional information about error 38 | public struct AWSErrorContext { 39 | public let message: String 40 | public let responseCode: HTTPResponseStatus 41 | public let headers: HTTPHeaders 42 | public let additionalFields: [String: String] 43 | 44 | internal init( 45 | message: String, 46 | responseCode: HTTPResponseStatus, 47 | headers: HTTPHeaders = [:], 48 | additionalFields: [String: String] = [:] 49 | ) { 50 | self.message = message 51 | self.responseCode = responseCode 52 | self.headers = headers 53 | self.additionalFields = additionalFields 54 | } 55 | } 56 | 57 | /// Standard Response Error type returned by Soto. If the error code is unrecognised then this is returned 58 | public struct AWSResponseError: AWSErrorType { 59 | public let errorCode: String 60 | public let context: AWSErrorContext? 61 | 62 | public init(errorCode: String, context: AWSErrorContext) { 63 | self.errorCode = errorCode 64 | self.context = context 65 | } 66 | 67 | public var description: String { 68 | return "\(self.errorCode): \(self.message ?? "")" 69 | } 70 | } 71 | 72 | /// Unrecognised error. Used when we cannot extract an error code from the AWS response. Returns full body of error response 73 | public struct AWSRawError: Error, CustomStringConvertible { 74 | public let rawBody: String? 75 | public let context: AWSErrorContext 76 | 77 | init(rawBody: String?, context: AWSErrorContext) { 78 | self.rawBody = rawBody 79 | self.context = context 80 | } 81 | 82 | public var description: String { 83 | return "Unhandled error, code: \(self.context.responseCode)\(self.rawBody.map { ", body: \($0)" } ?? "")" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Tests/SotoCoreTests/Credential/StaticCredential+EnvironmentTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import XCTest 16 | #if os(Linux) 17 | import Glibc 18 | #else 19 | import Darwin 20 | #endif 21 | @testable import SotoCore 22 | 23 | extension Environment { 24 | static func set(_ value: String, for name: String) { 25 | guard setenv(name, value, 1) == 0 else { 26 | XCTFail() 27 | return 28 | } 29 | } 30 | 31 | static func unset(name: String) { 32 | XCTAssertEqual(unsetenv(name), 0) 33 | } 34 | } 35 | 36 | class StaticCredential_EnvironmentTests: XCTestCase { 37 | override func tearDown() { 38 | Environment.unset(name: "AWS_ACCESS_KEY_ID") 39 | Environment.unset(name: "AWS_SECRET_ACCESS_KEY") 40 | Environment.unset(name: "AWS_SESSION_TOKEN") 41 | } 42 | 43 | func testSuccess() { 44 | let accessKeyId = "AWSACCESSKEYID" 45 | let secretAccessKet = "AWSSECRETACCESSKEY" 46 | let sessionToken = "AWSSESSIONTOKEN" 47 | 48 | Environment.set(accessKeyId, for: "AWS_ACCESS_KEY_ID") 49 | Environment.set(secretAccessKet, for: "AWS_SECRET_ACCESS_KEY") 50 | Environment.set(sessionToken, for: "AWS_SESSION_TOKEN") 51 | 52 | let cred = StaticCredential.fromEnvironment() 53 | 54 | XCTAssertEqual(cred?.accessKeyId, accessKeyId) 55 | XCTAssertEqual(cred?.secretAccessKey, secretAccessKet) 56 | XCTAssertEqual(cred?.sessionToken, sessionToken) 57 | } 58 | 59 | func testFailWithoutAccessKeyId() { 60 | let secretAccessKet = "AWSSECRETACCESSKEY" 61 | let sessionToken = "AWSSESSIONTOKEN" 62 | 63 | Environment.set(secretAccessKet, for: "AWS_SECRET_ACCESS_KEY") 64 | Environment.set(sessionToken, for: "AWS_SESSION_TOKEN") 65 | 66 | let cred = StaticCredential.fromEnvironment() 67 | 68 | XCTAssertNil(cred) 69 | } 70 | 71 | func testFailWithoutSecretAccessKey() { 72 | let accessKeyId = "AWSACCESSKEYID" 73 | let sessionToken = "AWSSESSIONTOKEN" 74 | 75 | Environment.set(accessKeyId, for: "AWS_ACCESS_KEY_ID") 76 | Environment.set(sessionToken, for: "AWS_SESSION_TOKEN") 77 | 78 | let cred = StaticCredential.fromEnvironment() 79 | 80 | XCTAssertNil(cred) 81 | } 82 | 83 | func testSuccessWithoutSessionToken() { 84 | let accessKeyId = "AWSACCESSKEYID" 85 | let secretAccessKet = "AWSSECRETACCESSKEY" 86 | 87 | Environment.set(accessKeyId, for: "AWS_ACCESS_KEY_ID") 88 | Environment.set(secretAccessKet, for: "AWS_SECRET_ACCESS_KEY") 89 | 90 | let cred = StaticCredential.fromEnvironment() 91 | 92 | XCTAssertEqual(cred?.accessKeyId, accessKeyId) 93 | XCTAssertEqual(cred?.secretAccessKey, secretAccessKet) 94 | XCTAssertNil(cred?.sessionToken) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Sources/SotoCore/HTTP/ResponseDelegate.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import AsyncHTTPClient 16 | import Foundation 17 | import NIO 18 | import NIOHTTP1 19 | 20 | /// HTTP client delegate capturing the body parts received from AsyncHTTPClient. 21 | class AWSHTTPClientResponseDelegate: HTTPClientResponseDelegate { 22 | typealias Response = AWSHTTPResponse 23 | 24 | enum State { 25 | case idle 26 | case head(HTTPResponseHead) 27 | case end 28 | case error(Error) 29 | } 30 | 31 | let host: String 32 | let stream: (ByteBuffer, EventLoop) -> EventLoopFuture 33 | var state: State 34 | 35 | init(host: String, stream: @escaping (ByteBuffer, EventLoop) -> EventLoopFuture) { 36 | self.host = host 37 | self.stream = stream 38 | self.state = .idle 39 | } 40 | 41 | func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { 42 | switch self.state { 43 | case .idle: 44 | self.state = .head(head) 45 | case .head: 46 | preconditionFailure("head already set") 47 | case .end: 48 | preconditionFailure("request already processed") 49 | case .error: 50 | break 51 | } 52 | return task.eventLoop.makeSucceededFuture(()) 53 | } 54 | 55 | func didReceiveBodyPart(task: HTTPClient.Task, _ part: ByteBuffer) -> EventLoopFuture { 56 | switch self.state { 57 | case .idle: 58 | preconditionFailure("no head received before body") 59 | case .head(let head): 60 | if (200..<300).contains(head.status.code) { 61 | let futureResult = self.stream(part, task.eventLoop) 62 | return futureResult 63 | } 64 | self.state = .head(head) 65 | case .end: 66 | preconditionFailure("request already processed") 67 | case .error: 68 | break 69 | } 70 | return task.eventLoop.makeSucceededFuture(()) 71 | } 72 | 73 | func didReceiveError(task: HTTPClient.Task, _ error: Error) { 74 | self.state = .error(error) 75 | } 76 | 77 | func didFinishRequest(task: HTTPClient.Task) throws -> AWSHTTPResponse { 78 | switch self.state { 79 | case .idle: 80 | preconditionFailure("no head received before end") 81 | case .head(let head): 82 | return AsyncHTTPClient.HTTPClient.Response( 83 | host: self.host, 84 | status: head.status, 85 | version: .init(major: 1, minor: 1), 86 | headers: head.headers, 87 | body: nil 88 | ) 89 | case .end: 90 | preconditionFailure("request already processed") 91 | case .error(let error): 92 | throw error 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Sources/SotoCore/Message/Body.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import NIO 16 | import NIOFoundationCompat 17 | import enum SotoXML.XML 18 | 19 | /// Enumaration used to store request/response body in various forms 20 | public enum Body { 21 | /// text 22 | case text(String) 23 | /// raw data 24 | case raw(AWSPayload) 25 | /// json data 26 | case json(ByteBuffer) 27 | /// xml 28 | case xml(XML.Element) 29 | /// empty body 30 | case empty 31 | } 32 | 33 | extension Body { 34 | /// return as a raw data buffer 35 | public func asString() -> String? { 36 | switch self { 37 | case .text(let text): 38 | return text 39 | 40 | case .raw(let payload): 41 | if let byteBuffer = payload.asByteBuffer() { 42 | return byteBuffer.getString(at: byteBuffer.readerIndex, length: byteBuffer.readableBytes, encoding: .utf8) 43 | } else { 44 | return nil 45 | } 46 | 47 | case .json(let buffer): 48 | return buffer.getString(at: buffer.readerIndex, length: buffer.readableBytes, encoding: .utf8) 49 | 50 | case .xml(let node): 51 | let xmlDocument = XML.Document(rootElement: node) 52 | return xmlDocument.xmlString 53 | 54 | case .empty: 55 | return nil 56 | } 57 | } 58 | 59 | /// return as payload 60 | public func asPayload(byteBufferAllocator: ByteBufferAllocator) -> AWSPayload { 61 | switch self { 62 | case .text(let text): 63 | var buffer = byteBufferAllocator.buffer(capacity: text.utf8.count) 64 | buffer.writeString(text) 65 | return .byteBuffer(buffer) 66 | 67 | case .raw(let payload): 68 | return payload 69 | 70 | case .json(let buffer): 71 | if buffer.readableBytes == 0 { 72 | return .empty 73 | } else { 74 | return .byteBuffer(buffer) 75 | } 76 | 77 | case .xml(let node): 78 | let xmlDocument = XML.Document(rootElement: node) 79 | let text = xmlDocument.xmlString 80 | var buffer = byteBufferAllocator.buffer(capacity: text.utf8.count) 81 | buffer.writeString(text) 82 | return .byteBuffer(buffer) 83 | 84 | case .empty: 85 | return .empty 86 | } 87 | } 88 | 89 | /// return as ByteBuffer 90 | public func asByteBuffer(byteBufferAllocator: ByteBufferAllocator) -> ByteBuffer? { 91 | return asPayload(byteBufferAllocator: byteBufferAllocator).asByteBuffer() 92 | } 93 | 94 | var isStreaming: Bool { 95 | if case .raw(let payload) = self, case .stream = payload.payload { 96 | return true 97 | } 98 | return false 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Sources/SotoCore/HTTP/StreamReader.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import NIO 16 | import NIOHTTP1 17 | 18 | /// Streaming result 19 | public enum StreamReaderResult { 20 | case byteBuffer(ByteBuffer) 21 | case end 22 | } 23 | 24 | /// Protocol for objects that supply streamed data to HTTPClient.Body.StreamWriter 25 | protocol StreamReader { 26 | /// size of data to be streamed 27 | var size: Int? { get } 28 | /// total size of data to be streamed plus any chunk headers 29 | var contentSize: Int? { get } 30 | /// function providing data to be streamed 31 | var read: (EventLoop) -> EventLoopFuture { get } 32 | /// bytebuffer allocator 33 | var byteBufferAllocator: ByteBufferAllocator { get } 34 | 35 | /// Update headers for this kind of streamed data 36 | /// - Parameter headers: headers to update 37 | func updateHeaders(headers: HTTPHeaders) -> HTTPHeaders 38 | 39 | /// Provide a list of ByteBuffers to write. Back pressure is applied on the last buffer 40 | /// - Parameter eventLoop: eventLoop to use when generating the event loop future 41 | func streamChunks(on eventLoop: EventLoop) -> EventLoopFuture<[ByteBuffer]> 42 | } 43 | 44 | /// Standard chunked streamer. Adds transfer-encoding : chunked header if a size is not supplied. NIO adds all the chunk headers 45 | /// so it just passes the streamed data straight through to the StreamWriter 46 | struct ChunkedStreamReader: StreamReader { 47 | /// Update headers. Add "Transfer-encoding" header if we don't have a steam size 48 | /// - Parameter headers: headers to update 49 | func updateHeaders(headers: HTTPHeaders) -> HTTPHeaders { 50 | var headers = headers 51 | // add "Transfer-Encoding" header if streaming with unknown size 52 | if self.size == nil { 53 | headers.add(name: "Transfer-Encoding", value: "chunked") 54 | } 55 | return headers 56 | } 57 | 58 | /// Provide a list of ByteBuffers to write. The `ChunkedStreamReader` just passes the `ByteBuffer` supplied to straight through 59 | /// - Parameter eventLoop: eventLoop to use when generating the event loop future 60 | func streamChunks(on eventLoop: EventLoop) -> EventLoopFuture<[ByteBuffer]> { 61 | return self.read(eventLoop).map { result -> [ByteBuffer] in 62 | switch result { 63 | case .byteBuffer(let byteBuffer): 64 | return [byteBuffer] 65 | case .end: 66 | return [] 67 | } 68 | } 69 | } 70 | 71 | /// Content size is the same as the size as we aren't adding any chunk headers here 72 | var contentSize: Int? { return self.size } 73 | 74 | /// size of data to be streamed 75 | let size: Int? 76 | /// function providing data to be streamed 77 | let read: (EventLoop) -> EventLoopFuture 78 | /// bytebuffer allocator 79 | var byteBufferAllocator: ByteBufferAllocator 80 | } 81 | -------------------------------------------------------------------------------- /Sources/SotoCore/Credential/DeferredCredentialProvider.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Logging 16 | import NIO 17 | import NIOConcurrencyHelpers 18 | 19 | /// Used for wrapping another credential provider whose `getCredential` method doesn't return instantly and 20 | /// is only needed to be called once. After the wrapped `CredentialProvider` has generated a credential this is 21 | /// returned instead of calling the wrapped `CredentialProvider's` `getCredentials` again. 22 | public class DeferredCredentialProvider: CredentialProvider { 23 | let lock = Lock() 24 | var credential: Credential? { 25 | get { 26 | self.lock.withLock { 27 | internalCredential 28 | } 29 | } 30 | set { 31 | self.lock.withLock { 32 | internalCredential = newValue 33 | } 34 | } 35 | } 36 | 37 | private var provider: CredentialProvider 38 | private var startupPromise: EventLoopPromise 39 | private var internalCredential: Credential? 40 | 41 | /// Create `DeferredCredentialProvider`. 42 | /// - Parameters: 43 | /// - eventLoop: EventLoop that getCredential should run on 44 | /// - provider: Credential provider to wrap 45 | public init(context: CredentialProviderFactory.Context, provider: CredentialProvider) { 46 | self.startupPromise = context.eventLoop.makePromise(of: Credential.self) 47 | self.provider = provider 48 | provider.getCredential(on: context.eventLoop, logger: context.logger) 49 | .flatMapErrorThrowing { _ in throw CredentialProviderError.noProvider } 50 | .map { credential in 51 | self.credential = credential 52 | context.logger.debug("AWS credentials ready", metadata: ["aws-credential-provider": .string("\(self)")]) 53 | return credential 54 | } 55 | .cascade(to: self.startupPromise) 56 | } 57 | 58 | /// Shutdown credential provider 59 | public func shutdown(on eventLoop: EventLoop) -> EventLoopFuture { 60 | return self.startupPromise.futureResult 61 | .and(self.provider.shutdown(on: eventLoop)) 62 | .map { _ in } 63 | .hop(to: eventLoop) 64 | } 65 | 66 | /// Return credentials. If still in process of the getting credentials then return future result of `startupPromise` 67 | /// otherwise return credentials store in class 68 | /// - Parameter eventLoop: EventLoop to run off 69 | /// - Returns: EventLoopFuture that will hold credentials 70 | public func getCredential(on eventLoop: EventLoop, logger: Logger) -> EventLoopFuture { 71 | if let credential = self.credential { 72 | return eventLoop.makeSucceededFuture(credential) 73 | } 74 | 75 | return self.startupPromise.futureResult.hop(to: eventLoop) 76 | } 77 | } 78 | 79 | extension DeferredCredentialProvider: CustomStringConvertible { 80 | public var description: String { return "\(type(of: self))(\(self.provider.description))" } 81 | } 82 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/asciitab.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __ _ 3 | ___\ \/ /_ __ __ _| |_ 4 | / _ \\ /| '_ \ / _` | __| 5 | | __// \| |_) | (_| | |_ 6 | \___/_/\_\ .__/ \__,_|\__| 7 | |_| XML parser 8 | 9 | Copyright (c) 1997-2000 Thai Open Source Software Center Ltd 10 | Copyright (c) 2000-2017 Expat development team 11 | Licensed under the MIT license: 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining 14 | a copy of this software and associated documentation files (the 15 | "Software"), to deal in the Software without restriction, including 16 | without limitation the rights to use, copy, modify, merge, publish, 17 | distribute, sublicense, and/or sell copies of the Software, and to permit 18 | persons to whom the Software is furnished to do so, subject to the 19 | following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included 22 | in all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 27 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 28 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 29 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 30 | USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 34 | /* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 35 | /* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, 36 | /* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, 37 | /* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 38 | /* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 39 | /* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 40 | /* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 41 | /* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, 42 | /* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, 43 | /* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, 44 | /* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, 45 | /* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, 46 | /* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, 47 | /* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, 48 | /* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, 49 | /* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, 50 | /* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, 51 | /* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 52 | /* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 53 | /* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 54 | /* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 55 | /* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, 56 | /* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, 57 | /* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, 58 | /* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, 59 | /* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 60 | /* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 61 | /* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 62 | /* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 63 | /* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, 64 | /* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, 65 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/utf8tab.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __ _ 3 | ___\ \/ /_ __ __ _| |_ 4 | / _ \\ /| '_ \ / _` | __| 5 | | __// \| |_) | (_| | |_ 6 | \___/_/\_\ .__/ \__,_|\__| 7 | |_| XML parser 8 | 9 | Copyright (c) 1997-2000 Thai Open Source Software Center Ltd 10 | Copyright (c) 2000-2017 Expat development team 11 | Licensed under the MIT license: 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining 14 | a copy of this software and associated documentation files (the 15 | "Software"), to deal in the Software without restriction, including 16 | without limitation the rights to use, copy, modify, merge, publish, 17 | distribute, sublicense, and/or sell copies of the Software, and to permit 18 | persons to whom the Software is furnished to do so, subject to the 19 | following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included 22 | in all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 27 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 28 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 29 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 30 | USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 34 | /* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 35 | /* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 36 | /* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 37 | /* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 38 | /* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 39 | /* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 40 | /* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 41 | /* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 42 | /* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 43 | /* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 44 | /* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 45 | /* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 46 | /* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 47 | /* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 48 | /* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, 49 | /* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, 50 | /* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, 51 | /* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, 52 | /* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, 53 | /* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, 54 | /* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, 55 | /* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, 56 | /* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, 57 | /* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, 58 | /* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, 59 | /* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, 60 | /* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, 61 | /* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, 62 | /* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, 63 | /* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 64 | /* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, 65 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/xmltok_impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __ _ 3 | ___\ \/ /_ __ __ _| |_ 4 | / _ \\ /| '_ \ / _` | __| 5 | | __// \| |_) | (_| | |_ 6 | \___/_/\_\ .__/ \__,_|\__| 7 | |_| XML parser 8 | 9 | Copyright (c) 1997-2000 Thai Open Source Software Center Ltd 10 | Copyright (c) 2000-2017 Expat development team 11 | Licensed under the MIT license: 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining 14 | a copy of this software and associated documentation files (the 15 | "Software"), to deal in the Software without restriction, including 16 | without limitation the rights to use, copy, modify, merge, publish, 17 | distribute, sublicense, and/or sell copies of the Software, and to permit 18 | persons to whom the Software is furnished to do so, subject to the 19 | following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included 22 | in all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 27 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 28 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 29 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 30 | USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | enum { 34 | BT_NONXML, /* e.g. noncharacter-FFFF */ 35 | BT_MALFORM, /* illegal, with regard to encoding */ 36 | BT_LT, /* less than = "<" */ 37 | BT_AMP, /* ampersand = "&" */ 38 | BT_RSQB, /* right square bracket = "[" */ 39 | BT_LEAD2, /* lead byte of a 2-byte UTF-8 character */ 40 | BT_LEAD3, /* lead byte of a 3-byte UTF-8 character */ 41 | BT_LEAD4, /* lead byte of a 4-byte UTF-8 character */ 42 | BT_TRAIL, /* trailing unit, e.g. second 16-bit unit of a 4-byte char. */ 43 | BT_CR, /* carriage return = "\r" */ 44 | BT_LF, /* line feed = "\n" */ 45 | BT_GT, /* greater than = ">" */ 46 | BT_QUOT, /* quotation character = "\"" */ 47 | BT_APOS, /* aposthrophe = "'" */ 48 | BT_EQUALS, /* equal sign = "=" */ 49 | BT_QUEST, /* question mark = "?" */ 50 | BT_EXCL, /* exclamation mark = "!" */ 51 | BT_SOL, /* solidus, slash = "/" */ 52 | BT_SEMI, /* semicolon = ";" */ 53 | BT_NUM, /* number sign = "#" */ 54 | BT_LSQB, /* left square bracket = "[" */ 55 | BT_S, /* white space, e.g. "\t", " "[, "\r"] */ 56 | BT_NMSTRT, /* non-hex name start letter = "G".."Z" + "g".."z" + "_" */ 57 | BT_COLON, /* colon = ":" */ 58 | BT_HEX, /* hex letter = "A".."F" + "a".."f" */ 59 | BT_DIGIT, /* digit = "0".."9" */ 60 | BT_NAME, /* dot and middle dot = "." + chr(0xb7) */ 61 | BT_MINUS, /* minus = "-" */ 62 | BT_OTHER, /* known not to be a name or name start character */ 63 | BT_NONASCII, /* might be a name or name start character */ 64 | BT_PERCNT, /* percent sign = "%" */ 65 | BT_LPAR, /* left parenthesis = "(" */ 66 | BT_RPAR, /* right parenthesis = "(" */ 67 | BT_AST, /* asterisk = "*" */ 68 | BT_PLUS, /* plus sign = "+" */ 69 | BT_COMMA, /* comma = "," */ 70 | BT_VERBAR /* vertical bar = "|" */ 71 | }; 72 | 73 | #include 74 | -------------------------------------------------------------------------------- /scripts/templates/generate-region/Region.stencil: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | // THIS FILE IS AUTOMATICALLY GENERATED by https://github.com/soto-project/soto-core/scripts/generate-region.swift. DO NOT EDIT. 16 | 17 | /// Enumeration for all AWS server regions 18 | public struct Region: RawRepresentable, Equatable { 19 | public var rawValue: String 20 | 21 | public init(rawValue: String) { 22 | self.rawValue = rawValue 23 | } 24 | 25 | {%for region in regions %} 26 | // {{region.description}} 27 | public static var {{region.enum}}: Region { .init(rawValue: "{{region.name}}") } 28 | {%endfor %} 29 | // other region 30 | public static func other(_ name: String) -> Region { .init(rawValue: name) } 31 | } 32 | 33 | extension Region { 34 | public var partition: AWSPartition { 35 | switch self { 36 | {%for region in regions %} 37 | case .{{region.enum}}: return .{{region.partition}} 38 | {%endfor %} 39 | default: return .aws 40 | } 41 | } 42 | } 43 | 44 | extension Region: CustomStringConvertible { 45 | public var description: String { return self.rawValue } 46 | } 47 | 48 | extension Region: Codable {} 49 | 50 | /// Enumeration for all AWS partitions 51 | public struct AWSPartition: RawRepresentable, Equatable, Hashable { 52 | enum InternalPartition: String { 53 | {%for partition in partitions %} 54 | case {{partition.name}} 55 | {%endfor %} 56 | } 57 | 58 | private var partition: InternalPartition 59 | 60 | public var rawValue: String { return self.partition.rawValue } 61 | 62 | public init?(rawValue: String) { 63 | guard let partition = InternalPartition(rawValue: rawValue) else { return nil } 64 | self.partition = partition 65 | } 66 | 67 | private init(partition: InternalPartition) { 68 | self.partition = partition 69 | } 70 | 71 | {%for partition in partitions %} 72 | // {{partition.description}} 73 | public static var {{partition.name}}: AWSPartition { .init(partition: .{{partition.name}}) } 74 | {%endfor %} 75 | } 76 | 77 | extension AWSPartition { 78 | public var dnsSuffix: String { 79 | switch self.partition { 80 | {%for partition in partitions %} 81 | case .{{partition.name}}: return "{{partition.dnsSuffix}}" 82 | {%endfor %} 83 | } 84 | } 85 | } 86 | 87 | // allows to create a Region from a String 88 | // it will only create a Region if the provided 89 | // region name is valid. 90 | extension Region { 91 | 92 | public init?(awsRegionName: String) { 93 | self.init(rawValue: awsRegionName) 94 | switch self { 95 | {%for region in regions %} 96 | {%if forloop.first %} 97 | case .{{region.enum}}, 98 | {%endif %} 99 | {%if not forloop.first and not forloop.last%} 100 | .{{region.enum}}, 101 | {%endif %} 102 | {%if forloop.last %} 103 | .{{region.enum}}: 104 | {%endif %} 105 | {%endfor %} 106 | return 107 | default: 108 | return nil 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/latin1tab.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __ _ 3 | ___\ \/ /_ __ __ _| |_ 4 | / _ \\ /| '_ \ / _` | __| 5 | | __// \| |_) | (_| | |_ 6 | \___/_/\_\ .__/ \__,_|\__| 7 | |_| XML parser 8 | 9 | Copyright (c) 1997-2000 Thai Open Source Software Center Ltd 10 | Copyright (c) 2000-2017 Expat development team 11 | Licensed under the MIT license: 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining 14 | a copy of this software and associated documentation files (the 15 | "Software"), to deal in the Software without restriction, including 16 | without limitation the rights to use, copy, modify, merge, publish, 17 | distribute, sublicense, and/or sell copies of the Software, and to permit 18 | persons to whom the Software is furnished to do so, subject to the 19 | following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included 22 | in all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 27 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 28 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 29 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 30 | USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 34 | /* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 35 | /* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 36 | /* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 37 | /* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 38 | /* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 39 | /* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 40 | /* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 41 | /* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 42 | /* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 43 | /* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, 44 | /* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 45 | /* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 46 | /* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, 47 | /* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, 48 | /* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, 49 | /* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 50 | /* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 51 | /* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 52 | /* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 53 | /* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 54 | /* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, 55 | /* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 56 | /* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 57 | /* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 58 | /* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 59 | /* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 60 | /* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 61 | /* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 62 | /* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, 63 | /* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 64 | /* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 65 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/iasciitab.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __ _ 3 | ___\ \/ /_ __ __ _| |_ 4 | / _ \\ /| '_ \ / _` | __| 5 | | __// \| |_) | (_| | |_ 6 | \___/_/\_\ .__/ \__,_|\__| 7 | |_| XML parser 8 | 9 | Copyright (c) 1997-2000 Thai Open Source Software Center Ltd 10 | Copyright (c) 2000-2017 Expat development team 11 | Licensed under the MIT license: 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining 14 | a copy of this software and associated documentation files (the 15 | "Software"), to deal in the Software without restriction, including 16 | without limitation the rights to use, copy, modify, merge, publish, 17 | distribute, sublicense, and/or sell copies of the Software, and to permit 18 | persons to whom the Software is furnished to do so, subject to the 19 | following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included 22 | in all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 27 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 28 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 29 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 30 | USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ 34 | /* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 35 | /* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 36 | /* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, 37 | /* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, 38 | /* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 39 | /* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 40 | /* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 41 | /* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, 42 | /* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, 43 | /* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, 44 | /* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, 45 | /* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, 46 | /* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, 47 | /* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, 48 | /* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, 49 | /* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, 50 | /* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, 51 | /* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, 52 | /* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 53 | /* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 54 | /* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 55 | /* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 56 | /* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, 57 | /* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, 58 | /* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, 59 | /* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, 60 | /* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 61 | /* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 62 | /* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 63 | /* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, 64 | /* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, 65 | /* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, 66 | -------------------------------------------------------------------------------- /Sources/SotoCore/HTTP/AsyncHTTPClient.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import AsyncHTTPClient 16 | import Logging 17 | import NIO 18 | import NIOHTTP1 19 | 20 | /// comply with AWSHTTPClient protocol 21 | extension AsyncHTTPClient.HTTPClient: AWSHTTPClient { 22 | /// Execute HTTP request 23 | /// - Parameters: 24 | /// - request: HTTP request 25 | /// - timeout: If execution is idle for longer than timeout then throw error 26 | /// - eventLoop: eventLoop to run request on 27 | /// - Returns: EventLoopFuture that will be fulfilled with request response 28 | public func execute(request: AWSHTTPRequest, timeout: TimeAmount, on eventLoop: EventLoop, logger: Logger) -> EventLoopFuture { 29 | let requestBody: AsyncHTTPClient.HTTPClient.Body? 30 | var requestHeaders = request.headers 31 | 32 | switch request.body.payload { 33 | case .byteBuffer(let byteBuffer): 34 | requestBody = .byteBuffer(byteBuffer) 35 | case .stream(let reader): 36 | requestHeaders = reader.updateHeaders(headers: requestHeaders) 37 | requestBody = .stream(length: reader.contentSize) { writer in 38 | return writer.write(reader: reader, on: eventLoop) 39 | } 40 | case .empty: 41 | requestBody = nil 42 | } 43 | do { 44 | let asyncRequest = try AsyncHTTPClient.HTTPClient.Request( 45 | url: request.url, 46 | method: request.method, 47 | headers: requestHeaders, 48 | body: requestBody 49 | ) 50 | return self.execute( 51 | request: asyncRequest, 52 | eventLoop: .delegate(on: eventLoop), 53 | deadline: .now() + timeout, 54 | logger: logger 55 | ).map { $0 } 56 | } catch { 57 | return eventLoopGroup.next().makeFailedFuture(error) 58 | } 59 | } 60 | 61 | public func execute(request: AWSHTTPRequest, timeout: TimeAmount, on eventLoop: EventLoop, logger: Logger, stream: @escaping ResponseStream) -> EventLoopFuture { 62 | let requestBody: AsyncHTTPClient.HTTPClient.Body? 63 | if case .byteBuffer(let body) = request.body.payload { 64 | requestBody = .byteBuffer(body) 65 | } else { 66 | requestBody = nil 67 | } 68 | do { 69 | let asyncRequest = try AsyncHTTPClient.HTTPClient.Request( 70 | url: request.url, 71 | method: request.method, 72 | headers: request.headers, 73 | body: requestBody 74 | ) 75 | let delegate = AWSHTTPClientResponseDelegate(host: asyncRequest.host, stream: stream) 76 | return self.execute( 77 | request: asyncRequest, 78 | delegate: delegate, 79 | eventLoop: .delegate(on: eventLoop), 80 | deadline: .now() + timeout, 81 | logger: logger 82 | ).futureResult 83 | } catch { 84 | return eventLoopGroup.next().makeFailedFuture(error) 85 | } 86 | } 87 | } 88 | 89 | extension AsyncHTTPClient.HTTPClient.Response: AWSHTTPResponse {} 90 | -------------------------------------------------------------------------------- /Sources/SotoCore/Encoder/CodableProperties/DateCoders.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import struct Foundation.Date 16 | import class Foundation.DateFormatter 17 | import struct Foundation.Locale 18 | import struct Foundation.TimeZone 19 | 20 | // MARK: TimeStamp Coders 21 | 22 | /// Protocol for time stamp coders that use a DateFormatter. Use this to enforce the timestamp format we require, or to set the timestamp format output 23 | protocol DateFormatCoder: CustomDecoder, CustomEncoder where CodableValue == Date { 24 | /// format used by DateFormatter 25 | static var formats: [String] { get } 26 | /// Date formatter 27 | static var dateFormatters: [DateFormatter] { get } 28 | } 29 | 30 | extension DateFormatCoder { 31 | /// decode Date using DateFormatter 32 | public static func decode(from decoder: Decoder) throws -> CodableValue { 33 | let container = try decoder.singleValueContainer() 34 | let value = try container.decode(String.self) 35 | for dateFormatter in dateFormatters { 36 | if let date = dateFormatter.date(from: value) { 37 | return date 38 | } 39 | } 40 | throw DecodingError.dataCorruptedError(in: container, debugDescription: "String is not the correct date format") 41 | } 42 | 43 | /// encode Date using DateFormatter 44 | public static func encode(value: CodableValue, to encoder: Encoder) throws { 45 | var container = encoder.singleValueContainer() 46 | try container.encode(dateFormatters[0].string(from: value)) 47 | } 48 | 49 | public static func string(from value: Date) -> String? { 50 | dateFormatters[0].string(from: value) 51 | } 52 | 53 | /// create DateFormatter 54 | static func createDateFormatters() -> [DateFormatter] { 55 | var dateFormatters: [DateFormatter] = [] 56 | precondition(formats.count > 0, "TimeStampFormatterCoder requires at least one format") 57 | for format in formats { 58 | let dateFormatter = DateFormatter() 59 | dateFormatter.locale = Locale(identifier: "en_US_POSIX") 60 | dateFormatter.dateFormat = format 61 | dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) 62 | dateFormatters.append(dateFormatter) 63 | } 64 | return dateFormatters 65 | } 66 | } 67 | 68 | /// Date coder for ISO8601 format 69 | public struct ISO8601DateCoder: DateFormatCoder { 70 | public static let formats = ["yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "yyyy-MM-dd'T'HH:mm:ss'Z'"] 71 | public static let dateFormatters = createDateFormatters() 72 | } 73 | 74 | /// Date coder for HTTP header format 75 | public struct HTTPHeaderDateCoder: DateFormatCoder { 76 | public static let formats = ["EEE, d MMM yyy HH:mm:ss z"] 77 | public static let dateFormatters = createDateFormatters() 78 | } 79 | 80 | /// Unix Epoch Date coder 81 | public struct UnixEpochDateCoder: CustomDecoder, CustomEncoder { 82 | public typealias CodableValue = Date 83 | 84 | public static func decode(from decoder: Decoder) throws -> CodableValue { 85 | let container = try decoder.singleValueContainer() 86 | let value = try container.decode(Double.self) 87 | return Date(timeIntervalSince1970: value) 88 | } 89 | 90 | public static func encode(value: CodableValue, to encoder: Encoder) throws { 91 | var container = encoder.singleValueContainer() 92 | try container.encode(value.timeIntervalSince1970) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Sources/SotoCore/HTTP/StreamWriter+write.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import AsyncHTTPClient 16 | import NIO 17 | 18 | extension AsyncHTTPClient.HTTPClient.Body.StreamWriter { 19 | /// write stream to StreamWriter 20 | func write( 21 | reader: StreamReader, 22 | on eventLoop: EventLoop 23 | ) -> EventLoopFuture { 24 | let promise = eventLoop.makePromise(of: Void.self) 25 | 26 | func _write(_ amountLeft: Int?) { 27 | // get byte buffer from closure, write to StreamWriter, if there are still bytes to write then call 28 | // _writeToStreamWriter again. 29 | reader.streamChunks(on: eventLoop) 30 | .map { (byteBuffers) -> Void in 31 | // if no amount was set and no byte buffers are supppied then this is assumed to mean 32 | // there will be no more data 33 | if amountLeft == nil, byteBuffers.count == 0 { 34 | promise.succeed(()) 35 | return 36 | } 37 | // calculate amount left to write 38 | let newAmountLeft: Int? 39 | if let amountLeft = amountLeft { 40 | guard byteBuffers.count > 0 else { 41 | promise.fail(AWSClient.ClientError.notEnoughData) 42 | return 43 | } 44 | let bytesToWrite = byteBuffers.reduce(0) { $0 + $1.readableBytes } 45 | newAmountLeft = amountLeft - bytesToWrite 46 | guard newAmountLeft! >= 0 else { 47 | promise.fail(AWSClient.ClientError.tooMuchData) 48 | return 49 | } 50 | } else { 51 | newAmountLeft = nil 52 | } 53 | 54 | // write all the chunks but the last. 55 | byteBuffers.dropLast().forEach { 56 | _ = self.write(.byteBuffer($0)) 57 | } 58 | if let lastBuffer = byteBuffers.last { 59 | // store EventLoopFuture of last byteBuffer 60 | let writeFuture: EventLoopFuture = self.write(.byteBuffer(lastBuffer)) 61 | writeFuture.flatMap { () -> EventLoopFuture in 62 | if let newAmountLeft = newAmountLeft { 63 | if newAmountLeft == 0 { 64 | promise.succeed(()) 65 | } else if newAmountLeft < 0 { 66 | // should never reach here as HTTPClient throws HTTPClientError.bodyLengthMismatch 67 | promise.fail(AWSClient.ClientError.tooMuchData) 68 | } else { 69 | _write(newAmountLeft) 70 | } 71 | } else { 72 | _write(nil) 73 | } 74 | return promise.futureResult 75 | }.cascadeFailure(to: promise) 76 | } else { 77 | _write(newAmountLeft) 78 | } 79 | }.cascadeFailure(to: promise) 80 | } 81 | _write(reader.contentSize) 82 | 83 | return promise.futureResult 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/ascii.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __ _ 3 | ___\ \/ /_ __ __ _| |_ 4 | / _ \\ /| '_ \ / _` | __| 5 | | __// \| |_) | (_| | |_ 6 | \___/_/\_\ .__/ \__,_|\__| 7 | |_| XML parser 8 | 9 | Copyright (c) 1997-2000 Thai Open Source Software Center Ltd 10 | Copyright (c) 2000-2017 Expat development team 11 | Licensed under the MIT license: 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining 14 | a copy of this software and associated documentation files (the 15 | "Software"), to deal in the Software without restriction, including 16 | without limitation the rights to use, copy, modify, merge, publish, 17 | distribute, sublicense, and/or sell copies of the Software, and to permit 18 | persons to whom the Software is furnished to do so, subject to the 19 | following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included 22 | in all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 27 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 28 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 29 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 30 | USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | #define ASCII_A 0x41 34 | #define ASCII_B 0x42 35 | #define ASCII_C 0x43 36 | #define ASCII_D 0x44 37 | #define ASCII_E 0x45 38 | #define ASCII_F 0x46 39 | #define ASCII_G 0x47 40 | #define ASCII_H 0x48 41 | #define ASCII_I 0x49 42 | #define ASCII_J 0x4A 43 | #define ASCII_K 0x4B 44 | #define ASCII_L 0x4C 45 | #define ASCII_M 0x4D 46 | #define ASCII_N 0x4E 47 | #define ASCII_O 0x4F 48 | #define ASCII_P 0x50 49 | #define ASCII_Q 0x51 50 | #define ASCII_R 0x52 51 | #define ASCII_S 0x53 52 | #define ASCII_T 0x54 53 | #define ASCII_U 0x55 54 | #define ASCII_V 0x56 55 | #define ASCII_W 0x57 56 | #define ASCII_X 0x58 57 | #define ASCII_Y 0x59 58 | #define ASCII_Z 0x5A 59 | 60 | #define ASCII_a 0x61 61 | #define ASCII_b 0x62 62 | #define ASCII_c 0x63 63 | #define ASCII_d 0x64 64 | #define ASCII_e 0x65 65 | #define ASCII_f 0x66 66 | #define ASCII_g 0x67 67 | #define ASCII_h 0x68 68 | #define ASCII_i 0x69 69 | #define ASCII_j 0x6A 70 | #define ASCII_k 0x6B 71 | #define ASCII_l 0x6C 72 | #define ASCII_m 0x6D 73 | #define ASCII_n 0x6E 74 | #define ASCII_o 0x6F 75 | #define ASCII_p 0x70 76 | #define ASCII_q 0x71 77 | #define ASCII_r 0x72 78 | #define ASCII_s 0x73 79 | #define ASCII_t 0x74 80 | #define ASCII_u 0x75 81 | #define ASCII_v 0x76 82 | #define ASCII_w 0x77 83 | #define ASCII_x 0x78 84 | #define ASCII_y 0x79 85 | #define ASCII_z 0x7A 86 | 87 | #define ASCII_0 0x30 88 | #define ASCII_1 0x31 89 | #define ASCII_2 0x32 90 | #define ASCII_3 0x33 91 | #define ASCII_4 0x34 92 | #define ASCII_5 0x35 93 | #define ASCII_6 0x36 94 | #define ASCII_7 0x37 95 | #define ASCII_8 0x38 96 | #define ASCII_9 0x39 97 | 98 | #define ASCII_TAB 0x09 99 | #define ASCII_SPACE 0x20 100 | #define ASCII_EXCL 0x21 101 | #define ASCII_QUOT 0x22 102 | #define ASCII_AMP 0x26 103 | #define ASCII_APOS 0x27 104 | #define ASCII_MINUS 0x2D 105 | #define ASCII_PERIOD 0x2E 106 | #define ASCII_COLON 0x3A 107 | #define ASCII_SEMI 0x3B 108 | #define ASCII_LT 0x3C 109 | #define ASCII_EQUALS 0x3D 110 | #define ASCII_GT 0x3E 111 | #define ASCII_LSQB 0x5B 112 | #define ASCII_RSQB 0x5D 113 | #define ASCII_UNDERSCORE 0x5F 114 | #define ASCII_LPAREN 0x28 115 | #define ASCII_RPAREN 0x29 116 | #define ASCII_FF 0x0C 117 | #define ASCII_SLASH 0x2F 118 | #define ASCII_HASH 0x23 119 | #define ASCII_PIPE 0x7C 120 | #define ASCII_COMMA 0x2C 121 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/expat_config.h: -------------------------------------------------------------------------------- 1 | /* expat_config.h. Generated from expat_config.h.in by configure. */ 2 | /* expat_config.h.in. Generated from configure.ac by autoheader. */ 3 | 4 | /* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ 5 | #define BYTEORDER 1234 6 | 7 | /* Define to 1 if you have the `arc4random' function. */ 8 | /* #undef HAVE_ARC4RANDOM */ 9 | 10 | /* Define to 1 if you have the `arc4random_buf' function. */ 11 | /* #undef HAVE_ARC4RANDOM_BUF */ 12 | 13 | /* Define to 1 if you have the `bcopy' function. */ 14 | #define HAVE_BCOPY 1 15 | 16 | /* Define to 1 if you have the header file. */ 17 | #define HAVE_DLFCN_H 1 18 | 19 | /* Define to 1 if you have the header file. */ 20 | #define HAVE_FCNTL_H 1 21 | 22 | /* Define to 1 if you have the `getpagesize' function. */ 23 | #define HAVE_GETPAGESIZE 1 24 | 25 | /* Define to 1 if you have the `getrandom' function. */ 26 | /* #undef HAVE_GETRANDOM */ 27 | 28 | /* Define to 1 if you have the header file. */ 29 | #define HAVE_INTTYPES_H 1 30 | 31 | /* Define to 1 if you have the `bsd' library (-lbsd). */ 32 | /* #undef HAVE_LIBBSD */ 33 | 34 | /* Define to 1 if you have the `memmove' function. */ 35 | #define HAVE_MEMMOVE 1 36 | 37 | /* Define to 1 if you have the header file. */ 38 | #define HAVE_MEMORY_H 1 39 | 40 | /* Define to 1 if you have a working `mmap' system call. */ 41 | #define HAVE_MMAP 1 42 | 43 | /* Define to 1 if you have the header file. */ 44 | #define HAVE_STDINT_H 1 45 | 46 | /* Define to 1 if you have the header file. */ 47 | #define HAVE_STDLIB_H 1 48 | 49 | /* Define to 1 if you have the header file. */ 50 | #define HAVE_STRINGS_H 1 51 | 52 | /* Define to 1 if you have the header file. */ 53 | #define HAVE_STRING_H 1 54 | 55 | /* Define to 1 if you have `syscall' and `SYS_getrandom'. */ 56 | /* #undef HAVE_SYSCALL_GETRANDOM */ 57 | 58 | /* Define to 1 if you have the header file. */ 59 | #define HAVE_SYS_PARAM_H 1 60 | 61 | /* Define to 1 if you have the header file. */ 62 | #define HAVE_SYS_STAT_H 1 63 | 64 | /* Define to 1 if you have the header file. */ 65 | #define HAVE_SYS_TYPES_H 1 66 | 67 | /* Define to 1 if you have the header file. */ 68 | #define HAVE_UNISTD_H 1 69 | 70 | /* Define to the sub-directory where libtool stores uninstalled libraries. */ 71 | #define LT_OBJDIR ".libs/" 72 | 73 | /* Name of package */ 74 | #define PACKAGE "expat" 75 | 76 | /* Define to the address where bug reports for this package should be sent. */ 77 | #define PACKAGE_BUGREPORT "expat-bugs@libexpat.org" 78 | 79 | /* Define to the full name of this package. */ 80 | #define PACKAGE_NAME "expat" 81 | 82 | /* Define to the full name and version of this package. */ 83 | #define PACKAGE_STRING "expat 2.2.9" 84 | 85 | /* Define to the one symbol short name of this package. */ 86 | #define PACKAGE_TARNAME "expat" 87 | 88 | /* Define to the home page for this package. */ 89 | #define PACKAGE_URL "" 90 | 91 | /* Define to the version of this package. */ 92 | #define PACKAGE_VERSION "2.2.9" 93 | 94 | /* Define to 1 if you have the ANSI C header files. */ 95 | #define STDC_HEADERS 1 96 | 97 | /* Version number of package */ 98 | #define VERSION "2.2.9" 99 | 100 | /* whether byteorder is bigendian */ 101 | /* #undef WORDS_BIGENDIAN */ 102 | 103 | /* Define to specify how much context to retain around the current parse 104 | point. */ 105 | #define XML_CONTEXT_BYTES 1024 106 | 107 | /* Define to include code reading entropy from `/dev/urandom'. */ 108 | #define XML_DEV_URANDOM 1 109 | 110 | /* Define to make parameter entity parsing functionality available. */ 111 | /* Do not need DTD code in Soto */ 112 | /*#define XML_DTD 1*/ 113 | 114 | /* Define to make XML Namespaces functionality available. */ 115 | /*#define XML_NS 1*/ 116 | 117 | /* Define to empty if `const' does not conform to ANSI C. */ 118 | /* #undef const */ 119 | 120 | /* Define to `long int' if does not define. */ 121 | /* #undef off_t */ 122 | 123 | /* Define to `unsigned int' if does not define. */ 124 | /* #undef size_t */ 125 | -------------------------------------------------------------------------------- /Sources/SotoCore/AWSService.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import struct Foundation.URL 16 | import NIO 17 | 18 | /// Protocol for services objects. Contains a client to communicate with AWS and config for defining how to communicate 19 | public protocol AWSService { 20 | /// Client used to communicate with AWS 21 | var client: AWSClient { get } 22 | /// Service context details 23 | var config: AWSServiceConfig { get } 24 | /// Patch initialization 25 | init(from: Self, patch: AWSServiceConfig.Patch) 26 | } 27 | 28 | extension AWSService { 29 | /// Region where service is running 30 | public var region: Region { return config.region } 31 | /// The url to use in requests 32 | public var endpoint: String { return config.endpoint } 33 | /// The EventLoopGroup service is using 34 | public var eventLoopGroup: EventLoopGroup { return client.eventLoopGroup } 35 | 36 | /// Generate a signed URL 37 | /// - parameters: 38 | /// - url : URL to sign 39 | /// - httpMethod: HTTP method to use (.GET, .PUT, .PUSH etc) 40 | /// - headers: Headers that are to be used with this URL. Be sure to include these headers when you used the returned URL 41 | /// - expires: How long before the signed URL expires 42 | /// - logger: Logger to output to 43 | /// - returns: 44 | /// A signed URL 45 | public func signURL( 46 | url: URL, 47 | httpMethod: HTTPMethod, 48 | headers: HTTPHeaders = HTTPHeaders(), 49 | expires: TimeAmount, 50 | logger: Logger = AWSClient.loggingDisabled 51 | ) -> EventLoopFuture { 52 | return self.client.signURL(url: url, httpMethod: httpMethod, headers: headers, expires: expires, serviceConfig: self.config, logger: logger) 53 | } 54 | 55 | /// Generate signed headers 56 | /// - parameters: 57 | /// - url : URL to sign 58 | /// - httpMethod: HTTP method to use (.GET, .PUT, .PUSH etc) 59 | /// - headers: Headers that are to be used with this URL. Be sure to include these headers when you used the returned URL 60 | /// - body: body payload to sign as well. While it is unnecessary to provide the body for S3 other services require it 61 | /// - logger: Logger to output to 62 | /// - returns: 63 | /// A series of signed headers including the original headers provided to the function 64 | public func signHeaders( 65 | url: URL, 66 | httpMethod: HTTPMethod, 67 | headers: HTTPHeaders = HTTPHeaders(), 68 | body: AWSPayload = .empty, 69 | logger: Logger = AWSClient.loggingDisabled 70 | ) -> EventLoopFuture { 71 | return self.client.signHeaders(url: url, httpMethod: httpMethod, headers: headers, body: body, serviceConfig: self.config, logger: logger) 72 | } 73 | 74 | /// Return new version of Service with edited parameters 75 | /// - Parameters: 76 | /// - middlewares: Additional middleware to add 77 | /// - timeout: Time out value for HTTP requests 78 | /// - byteBufferAllocator: byte buffer allocator used throughout AWSClient 79 | /// - options: options used by client when processing requests 80 | /// - Returns: New version of the service 81 | public func with( 82 | middlewares: [AWSServiceMiddleware] = [], 83 | timeout: TimeAmount? = nil, 84 | byteBufferAllocator: ByteBufferAllocator? = nil, 85 | options: AWSServiceConfig.Options? = nil 86 | ) -> Self { 87 | return Self(from: self, patch: .init( 88 | middlewares: middlewares, 89 | timeout: timeout, 90 | byteBufferAllocator: byteBufferAllocator, 91 | options: options 92 | )) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /scripts/generate-region.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env swift sh 2 | //===----------------------------------------------------------------------===// 3 | // 4 | // This source file is part of the Soto for AWS open source project 5 | // 6 | // Copyright (c) 2017-2020 the Soto project authors 7 | // Licensed under Apache License v2.0 8 | // 9 | // See LICENSE.txt for license information 10 | // See CONTRIBUTORS.txt for the list of Soto project authors 11 | // 12 | // SPDX-License-Identifier: Apache-2.0 13 | // 14 | //===----------------------------------------------------------------------===// 15 | 16 | import AsyncHTTPClient // swift-server/async-http-client 17 | import Foundation 18 | import NIO // apple/swift-nio 19 | import NIOFoundationCompat 20 | import Stencil // soto-project/Stencil 21 | 22 | struct Endpoints: Decodable { 23 | struct CredentialScope: Decodable { 24 | var region: String? 25 | var service: String? 26 | } 27 | 28 | struct Defaults: Decodable { 29 | var credentialScope: CredentialScope? 30 | var hostname: String? 31 | var protocols: [String]? 32 | var signatureVersions: [String]? 33 | } 34 | 35 | struct RegionDesc: Decodable { 36 | var description: String 37 | } 38 | 39 | struct Partition: Decodable { 40 | var defaults: Defaults 41 | var dnsSuffix: String 42 | var partition: String 43 | var partitionName: String 44 | var regionRegex: String 45 | var regions: [String: RegionDesc] 46 | } 47 | 48 | var partitions: [Partition] 49 | } 50 | 51 | struct RegionDesc { 52 | let `enum`: String 53 | let name: String 54 | let description: String? 55 | let partition: String 56 | } 57 | 58 | struct Partition { 59 | let name: String 60 | let description: String 61 | let dnsSuffix: String 62 | } 63 | 64 | func loadEndpoints(url: String) throws -> Endpoints? { 65 | let httpClient = HTTPClient(eventLoopGroupProvider: .createNew) 66 | defer { 67 | try? httpClient.syncShutdown() 68 | } 69 | let response = try httpClient.get(url: url, deadline: .now() + .seconds(10)).wait() 70 | if let body = response.body { 71 | let endpoints = try JSONDecoder().decode(Endpoints.self, from: body) 72 | return endpoints 73 | } 74 | return nil 75 | } 76 | 77 | print("Loading Endpoints") 78 | guard let endpoints = try loadEndpoints(url: "https://raw.githubusercontent.com/aws/aws-sdk-go/master/models/endpoints/endpoints.json") else { exit(-1) } 79 | 80 | var regionDescs: [RegionDesc] = [] 81 | var partitions: [Partition] = endpoints.partitions.map { 82 | return Partition( 83 | name: $0.partition.filter { return $0.isLetter || $0.isNumber }, 84 | description: $0.partitionName, 85 | dnsSuffix: $0.dnsSuffix 86 | ) 87 | } 88 | 89 | for partition in endpoints.partitions { 90 | let partitionRegionDescs = partition.regions.keys.map { region in 91 | return RegionDesc( 92 | enum: region.filter { return $0.isLetter || $0.isNumber }, 93 | name: region, 94 | description: partition.regions[region]?.description, 95 | partition: partition.partition.filter { return $0.isLetter || $0.isNumber } 96 | ) 97 | } 98 | regionDescs += partitionRegionDescs 99 | } 100 | 101 | // Add ap-northeast-3 as it isn't in the endpoints.json. It is intentionally excluded from endpoints as it requires access request. 102 | regionDescs.append(.init(enum: "apnortheast3", name: "ap-northeast-3", description: "Asia Pacific (Osaka Local)", partition: "aws")) 103 | 104 | print("Loading templates") 105 | let fsLoader = FileSystemLoader(paths: ["./scripts/templates/generate-region"]) 106 | let environment = Environment(loader: fsLoader) 107 | 108 | print("Creating Region.swift") 109 | 110 | let context: [String: Any] = [ 111 | "regions": regionDescs.sorted { $0.name < $1.name }, 112 | "partitions": partitions, 113 | ] 114 | 115 | let regionsFile = try environment.renderTemplate(name: "Region.stencil", context: context) 116 | try Data(regionsFile.utf8).write(to: URL(fileURLWithPath: "Sources/SotoCore/Doc/Region.swift")) 117 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | //===----------------------------------------------------------------------===// 3 | // 4 | // This source file is part of the Soto for AWS open source project 5 | // 6 | // Copyright (c) 2017-2020 the Soto project authors 7 | // Licensed under Apache License v2.0 8 | // 9 | // See LICENSE.txt for license information 10 | // See CONTRIBUTORS.txt for the list of Soto project authors 11 | // 12 | // SPDX-License-Identifier: Apache-2.0 13 | // 14 | //===----------------------------------------------------------------------===// 15 | 16 | import PackageDescription 17 | 18 | let package = Package( 19 | name: "soto-core", 20 | products: [ 21 | .library(name: "SotoCore", targets: ["SotoCore"]), 22 | .library(name: "SotoTestUtils", targets: ["SotoTestUtils"]), 23 | .library(name: "SotoSignerV4", targets: ["SotoSignerV4"]), 24 | ], 25 | dependencies: [ 26 | .package(url: "https://github.com/apple/swift-log.git", from: "1.4.0"), 27 | .package(url: "https://github.com/apple/swift-metrics.git", "1.0.0"..<"3.0.0"), 28 | .package(url: "https://github.com/apple/swift-nio.git", .upToNextMajor(from: "2.16.1")), 29 | .package(url: "https://github.com/apple/swift-nio-ssl.git", .upToNextMajor(from: "2.7.2")), 30 | .package(url: "https://github.com/apple/swift-nio-transport-services.git", .upToNextMajor(from: "1.0.0")), 31 | .package(url: "https://github.com/swift-server/async-http-client.git", .upToNextMajor(from: "1.3.0")), 32 | ], 33 | targets: [ 34 | .target(name: "SotoCore", dependencies: [ 35 | .byName(name: "SotoSignerV4"), 36 | .byName(name: "SotoXML"), 37 | .byName(name: "INIParser"), 38 | .product(name: "Logging", package: "swift-log"), 39 | .product(name: "AsyncHTTPClient", package: "async-http-client"), 40 | .product(name: "Metrics", package: "swift-metrics"), 41 | .product(name: "NIO", package: "swift-nio"), 42 | .product(name: "NIOHTTP1", package: "swift-nio"), 43 | .product(name: "NIOSSL", package: "swift-nio-ssl"), 44 | .product(name: "NIOTransportServices", package: "swift-nio-transport-services"), 45 | .product(name: "NIOFoundationCompat", package: "swift-nio"), 46 | ]), 47 | .target(name: "SotoCrypto", dependencies: []), 48 | .target(name: "SotoSignerV4", dependencies: [ 49 | .byName(name: "SotoCrypto"), 50 | .product(name: "NIOHTTP1", package: "swift-nio"), 51 | ]), 52 | .target(name: "SotoTestUtils", dependencies: [ 53 | .byName(name: "SotoCore"), 54 | .product(name: "NIO", package: "swift-nio"), 55 | .product(name: "NIOHTTP1", package: "swift-nio"), 56 | .product(name: "NIOFoundationCompat", package: "swift-nio"), 57 | .product(name: "NIOTestUtils", package: "swift-nio"), 58 | ]), 59 | .target(name: "SotoXML", dependencies: [ 60 | .byName(name: "CSotoExpat"), 61 | ]), 62 | .target(name: "CSotoExpat", dependencies: []), 63 | .target(name: "INIParser", dependencies: []), 64 | 65 | .testTarget(name: "SotoCryptoTests", dependencies: [ 66 | .byName(name: "SotoCrypto"), 67 | ]), 68 | .testTarget(name: "SotoCoreTests", dependencies: [ 69 | .byName(name: "SotoCore"), 70 | .byName(name: "SotoTestUtils"), 71 | ]), 72 | .testTarget(name: "SotoSignerV4Tests", dependencies: [ 73 | .byName(name: "SotoSignerV4"), 74 | ]), 75 | .testTarget(name: "SotoXMLTests", dependencies: [ 76 | .byName(name: "SotoXML"), 77 | .byName(name: "SotoCore"), 78 | ]), 79 | .testTarget(name: "INIParserTests", dependencies: [ 80 | .byName(name: "INIParser"), 81 | ]), 82 | ] 83 | ) 84 | 85 | // switch for whether to use swift crypto. Swift crypto requires macOS10.15 or iOS13.I'd rather not pass this requirement on 86 | #if os(Linux) 87 | let useSwiftCrypto = true 88 | #else 89 | let useSwiftCrypto = false 90 | #endif 91 | 92 | // Use Swift cypto on Linux. 93 | if useSwiftCrypto { 94 | package.dependencies.append(.package(url: "https://github.com/apple/swift-crypto.git", from: "1.0.0")) 95 | package.targets.first { $0.name == "SotoCrypto" }?.dependencies.append(.product(name: "Crypto", package: "swift-crypto")) 96 | } 97 | -------------------------------------------------------------------------------- /Tests/SotoCoreTests/PayloadTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import NIO 16 | @testable import SotoCore 17 | import SotoTestUtils 18 | import XCTest 19 | 20 | class PayloadTests: XCTestCase { 21 | func testRequestPayload(_ payload: AWSPayload, expectedResult: String) { 22 | struct DataPayload: AWSEncodableShape & AWSShapeWithPayload { 23 | static var _payloadPath: String = "data" 24 | let data: AWSPayload 25 | 26 | private enum CodingKeys: CodingKey {} 27 | } 28 | 29 | do { 30 | let awsServer = AWSTestServer(serviceProtocol: .json) 31 | let config = createServiceConfig(endpoint: awsServer.address) 32 | let client = createAWSClient(credentialProvider: .empty) 33 | defer { 34 | XCTAssertNoThrow(try client.syncShutdown()) 35 | } 36 | let input = DataPayload(data: payload) 37 | let response = client.execute( 38 | operation: "test", 39 | path: "/", 40 | httpMethod: .POST, 41 | serviceConfig: config, 42 | input: input, 43 | logger: TestEnvironment.logger 44 | ) 45 | 46 | try awsServer.processRaw { request in 47 | XCTAssertEqual(request.body.getString(at: 0, length: request.body.readableBytes), expectedResult) 48 | return .result(.ok) 49 | } 50 | 51 | try response.wait() 52 | try awsServer.stop() 53 | } catch { 54 | XCTFail("Unexpected error: \(error)") 55 | } 56 | } 57 | 58 | func testDataRequestPayload() { 59 | self.testRequestPayload(.data(Data("testDataPayload".utf8)), expectedResult: "testDataPayload") 60 | } 61 | 62 | func testStringRequestPayload() { 63 | self.testRequestPayload(.string("testStringPayload"), expectedResult: "testStringPayload") 64 | } 65 | 66 | func testByteBufferRequestPayload() { 67 | var byteBuffer = ByteBufferAllocator().buffer(capacity: 32) 68 | byteBuffer.writeString("testByteBufferPayload") 69 | self.testRequestPayload(.byteBuffer(byteBuffer), expectedResult: "testByteBufferPayload") 70 | } 71 | 72 | func testResponsePayload() { 73 | struct Output: AWSDecodableShape, AWSShapeWithPayload { 74 | static let _payloadPath: String = "payload" 75 | static let _payloadOptions: AWSShapePayloadOptions = .raw 76 | let payload: AWSPayload 77 | } 78 | do { 79 | let awsServer = AWSTestServer(serviceProtocol: .json) 80 | let config = createServiceConfig(endpoint: awsServer.address) 81 | let client = createAWSClient(credentialProvider: .empty) 82 | defer { 83 | XCTAssertNoThrow(try client.syncShutdown()) 84 | } 85 | let response: EventLoopFuture = client.execute( 86 | operation: "test", 87 | path: "/", 88 | httpMethod: .POST, 89 | serviceConfig: config, 90 | logger: TestEnvironment.logger 91 | ) 92 | 93 | try awsServer.processRaw { _ in 94 | var byteBuffer = ByteBufferAllocator().buffer(capacity: 0) 95 | byteBuffer.writeString("testResponsePayload") 96 | let response = AWSTestServer.Response(httpStatus: .ok, headers: [:], body: byteBuffer) 97 | return .result(response) 98 | } 99 | 100 | let output = try response.wait() 101 | 102 | XCTAssertEqual(output.payload.asString(), "testResponsePayload") 103 | // XCTAssertEqual(output.i, 547) 104 | try awsServer.stop() 105 | } catch { 106 | XCTFail("Unexpected error: \(error)") 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /scripts/sanity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the SwiftNIO open source project 5 | ## 6 | ## Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 11 | ## 12 | ## SPDX-License-Identifier: Apache-2.0 13 | ## 14 | ##===----------------------------------------------------------------------===## 15 | 16 | SWIFT_VERSION=5.1 17 | 18 | set -eu 19 | here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 20 | 21 | which swiftformat > /dev/null 2>&1 || (echo "swiftformat not installed. You can install it using 'brew install swiftformat'" ; exit -1) 22 | 23 | function replace_acceptable_years() { 24 | # this needs to replace all acceptable forms with 'YEARS' 25 | sed -e 's/20[12][78901]-20[12][8901]/YEARS/' -e 's/20[12][8901]/YEARS/' -e '/^#!/ d' 26 | } 27 | 28 | printf "=> Checking format... " 29 | FIRST_OUT="$(git status --porcelain)" 30 | if [[ -n "${CI-""}" ]]; then 31 | printf "(using v$(mint run NickLockwood/SwiftFormat@0.47.13 --version)) " 32 | mint run NickLockwood/SwiftFormat@0.47.13 . > /dev/null 2>&1 33 | else 34 | printf "(using v$(swiftformat --version)) " 35 | swiftformat . > /dev/null 2>&1 36 | fi 37 | SECOND_OUT="$(git status --porcelain)" 38 | if [[ "$FIRST_OUT" != "$SECOND_OUT" ]]; then 39 | printf "\033[0;31mformatting issues!\033[0m\n" 40 | git --no-pager diff 41 | exit 1 42 | else 43 | printf "\033[0;32mokay.\033[0m\n" 44 | fi 45 | 46 | printf "=> Checking license headers... " 47 | tmp=$(mktemp /tmp/.soto-core-sanity_XXXXXX) 48 | 49 | for language in swift-or-c; do 50 | declare -a matching_files 51 | declare -a exceptions 52 | expections=( ) 53 | matching_files=( -name '*' ) 54 | case "$language" in 55 | swift-or-c) 56 | exceptions=( -path '*Sources/INIParser/*' -o -path '*Sources/CSotoExpat/*' -o -path '*Benchmark/.build/*' -o -name Package.swift) 57 | matching_files=( -name '*.swift' -o -name '*.c' -o -name '*.h' ) 58 | cat > "$tmp" <<"EOF" 59 | //===----------------------------------------------------------------------===// 60 | // 61 | // This source file is part of the Soto for AWS open source project 62 | // 63 | // Copyright (c) YEARS the Soto project authors 64 | // Licensed under Apache License v2.0 65 | // 66 | // See LICENSE.txt for license information 67 | // See CONTRIBUTORS.txt for the list of Soto project authors 68 | // 69 | // SPDX-License-Identifier: Apache-2.0 70 | // 71 | //===----------------------------------------------------------------------===// 72 | EOF 73 | ;; 74 | bash) 75 | matching_files=( -name '*.sh' ) 76 | cat > "$tmp" <<"EOF" 77 | ##===----------------------------------------------------------------------===## 78 | ## 79 | ## This source file is part of the Soto for AWS open source project 80 | ## 81 | ## Copyright (c) YEARS the Soto project authors 82 | ## Licensed under Apache License v2.0 83 | ## 84 | ## See LICENSE.txt for license information 85 | ## See CONTRIBUTORS.txt for the list of Soto project authors 86 | ## 87 | ## SPDX-License-Identifier: Apache-2.0 88 | ## 89 | ##===----------------------------------------------------------------------===## 90 | EOF 91 | ;; 92 | *) 93 | echo >&2 "ERROR: unknown language '$language'" 94 | ;; 95 | esac 96 | 97 | lines_to_compare=$(cat "$tmp" | wc -l | tr -d " ") 98 | # need to read one more line as we remove the '#!' line 99 | lines_to_read=$(expr "$lines_to_compare" + 1) 100 | expected_sha=$(cat "$tmp" | shasum) 101 | 102 | ( 103 | cd "$here/.." 104 | find . \ 105 | \( \! -path './.build/*' -a \ 106 | \( "${matching_files[@]}" \) -a \ 107 | \( \! \( "${exceptions[@]}" \) \) \) | while read line; do 108 | if [[ "$(cat "$line" | head -n $lines_to_read | replace_acceptable_years | head -n $lines_to_compare | shasum)" != "$expected_sha" ]]; then 109 | printf "\033[0;31mmissing headers in file '$line'!\033[0m\n" 110 | diff -u <(cat "$line" | head -n $lines_to_read | replace_acceptable_years | head -n $lines_to_compare) "$tmp" 111 | exit 1 112 | fi 113 | done 114 | printf "\033[0;32mokay.\033[0m\n" 115 | ) 116 | done 117 | 118 | rm "$tmp" 119 | -------------------------------------------------------------------------------- /Benchmark/Sources/soto-benchmark/AWSClientSuite.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Benchmark 16 | import Dispatch 17 | import Foundation 18 | import NIO 19 | import SotoCore 20 | 21 | struct RequestThrowMiddleware: AWSServiceMiddleware { 22 | struct Error: Swift.Error {} 23 | 24 | func chain(request: AWSRequest, context: AWSMiddlewareContext) throws -> AWSRequest { 25 | _ = request.body.asByteBuffer(byteBufferAllocator: ByteBufferAllocator()) 26 | throw Error() 27 | } 28 | } 29 | 30 | struct HeaderShape: AWSEncodableShape { 31 | static let _encoding: [AWSMemberEncoding] = [ 32 | .init(label: "a", location: .header(locationName: "A")), 33 | .init(label: "b", location: .header(locationName: "B")), 34 | ] 35 | let a: String 36 | let b: Int 37 | } 38 | 39 | struct QueryShape: AWSEncodableShape { 40 | static let _encoding: [AWSMemberEncoding] = [ 41 | .init(label: "a", location: .querystring(locationName: "A")), 42 | .init(label: "b", location: .querystring(locationName: "B")), 43 | ] 44 | let a: String 45 | let b: Int 46 | } 47 | 48 | struct Shape1: AWSEncodableShape { 49 | let a: String 50 | let b: Int 51 | } 52 | 53 | struct Shape: AWSEncodableShape { 54 | let a: String 55 | let b: Int 56 | let c: [String] 57 | let d: [String: Int] 58 | } 59 | 60 | let awsClientSuite = BenchmarkSuite(name: "AWSClient", settings: Iterations(10000), WarmupIterations(2)) { suite in 61 | // time request construction by throwing an error in request middleware. This means waiting on client.execute should 62 | // take the amount of time it took to construct the request 63 | let client = AWSClient( 64 | credentialProvider: .static(accessKeyId: "foo", secretAccessKey: "bar"), 65 | middlewares: [RequestThrowMiddleware()], 66 | httpClientProvider: .createNew 67 | ) 68 | let jsonService = AWSServiceConfig( 69 | region: .useast1, partition: .aws, service: "test-service", serviceProtocol: .json(version: "1.1"), apiVersion: "10-10-2010" 70 | ) 71 | let xmlService = AWSServiceConfig( 72 | region: .useast1, partition: .aws, service: "test-service", serviceProtocol: .restxml, apiVersion: "10-10-2010" 73 | ) 74 | let queryService = AWSServiceConfig( 75 | region: .useast1, partition: .aws, service: "test-service", serviceProtocol: .query, apiVersion: "10-10-2010" 76 | ) 77 | 78 | suite.benchmark("empty-request") { 79 | try? client.execute(operation: "TestOperation", path: "/", httpMethod: .GET, serviceConfig: jsonService).wait() 80 | } 81 | 82 | let headerInput = HeaderShape(a: "TestString", b: 345_348) 83 | suite.benchmark("header-request") { 84 | try? client.execute(operation: "TestOperation", path: "/", httpMethod: .GET, serviceConfig: jsonService, input: headerInput).wait() 85 | } 86 | 87 | let queryInput = QueryShape(a: "TestString", b: 345_348) 88 | suite.benchmark("querystring-request") { 89 | try? client.execute(operation: "TestOperation", path: "/", httpMethod: .GET, serviceConfig: jsonService, input: queryInput).wait() 90 | } 91 | 92 | // test json, xml and query generation timing 93 | let input = Shape(a: "TestString", b: 345_348, c: ["one", "two", "three"], d: ["one": 1, "two": 2, "three": 3]) 94 | suite.benchmark("json-request") { 95 | try? client.execute(operation: "TestOperation", path: "/", httpMethod: .GET, serviceConfig: jsonService, input: input).wait() 96 | } 97 | suite.benchmark("xml-request") { 98 | try? client.execute(operation: "TestOperation", path: "/", httpMethod: .GET, serviceConfig: xmlService, input: input).wait() 99 | } 100 | suite.benchmark("query-request") { 101 | try? client.execute(operation: "TestOperation", path: "/", httpMethod: .GET, serviceConfig: queryService, input: input).wait() 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Sources/SotoTestUtils/TestUtils.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Foundation 16 | import Logging 17 | import SotoCore 18 | 19 | @propertyWrapper public struct EnvironmentVariable { 20 | var defaultValue: Value 21 | var variableName: String 22 | 23 | public init(_ variableName: String, default: Value) { 24 | self.defaultValue = `default` 25 | self.variableName = variableName 26 | } 27 | 28 | public var wrappedValue: Value { 29 | guard let value = Environment[variableName] else { return self.defaultValue } 30 | return Value(value) ?? self.defaultValue 31 | } 32 | } 33 | 34 | public func createAWSClient( 35 | credentialProvider: CredentialProviderFactory = .default, 36 | retryPolicy: RetryPolicyFactory = .noRetry, 37 | middlewares: [AWSServiceMiddleware] = TestEnvironment.middlewares, 38 | options: AWSClient.Options = .init(), 39 | httpClientProvider: AWSClient.HTTPClientProvider = .createNew, 40 | logger: Logger = TestEnvironment.logger 41 | ) -> AWSClient { 42 | return AWSClient( 43 | credentialProvider: credentialProvider, 44 | retryPolicy: retryPolicy, 45 | middlewares: middlewares, 46 | options: options, 47 | httpClientProvider: httpClientProvider, 48 | logger: logger 49 | ) 50 | } 51 | 52 | public func createServiceConfig( 53 | region: Region? = nil, 54 | partition: AWSPartition = .aws, 55 | amzTarget: String? = nil, 56 | service: String = "test", 57 | signingName: String? = nil, 58 | serviceProtocol: ServiceProtocol = .restjson, 59 | apiVersion: String = "01-01-2001", 60 | endpoint: String? = nil, 61 | serviceEndpoints: [String: String] = [:], 62 | partitionEndpoints: [AWSPartition: (endpoint: String, region: Region)] = [:], 63 | errorType: AWSErrorType.Type? = nil, 64 | middlewares: [AWSServiceMiddleware] = [], 65 | timeout: TimeAmount? = nil 66 | ) -> AWSServiceConfig { 67 | AWSServiceConfig( 68 | region: region, 69 | partition: partition, 70 | amzTarget: amzTarget, 71 | service: service, 72 | signingName: signingName, 73 | serviceProtocol: serviceProtocol, 74 | apiVersion: apiVersion, 75 | endpoint: endpoint, 76 | serviceEndpoints: serviceEndpoints, 77 | partitionEndpoints: partitionEndpoints, 78 | errorType: errorType, 79 | middlewares: middlewares, 80 | timeout: timeout 81 | ) 82 | } 83 | 84 | // create a buffer of random values. Will always create the same given you supply the same z and w values 85 | // Random number generator from https://www.codeproject.com/Articles/25172/Simple-Random-Number-Generation 86 | public func createRandomBuffer(_ w: UInt, _ z: UInt, size: Int) -> [UInt8] { 87 | var z = z 88 | var w = w 89 | func getUInt8() -> UInt8 { 90 | z = 36969 * (z & 65535) + (z >> 16) 91 | w = 18000 * (w & 65535) + (w >> 16) 92 | return UInt8(((z << 16) + w) & 0xFF) 93 | } 94 | var data = [UInt8](repeating: 0, count: size) 95 | for i in 0.. Self.Digest { 32 | var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount) 33 | CC_SHA256(bufferPointer.baseAddress, CC_LONG(bufferPointer.count), &digest) 34 | return .init(bytes: digest) 35 | } 36 | 37 | public init() { 38 | self.context = CC_SHA256_CTX() 39 | CC_SHA256_Init(&self.context) 40 | } 41 | 42 | public mutating func update(bufferPointer: UnsafeRawBufferPointer) { 43 | CC_SHA256_Update(&self.context, bufferPointer.baseAddress, CC_LONG(bufferPointer.count)) 44 | } 45 | 46 | public mutating func finalize() -> Self.Digest { 47 | var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount) 48 | CC_SHA256_Final(&digest, &self.context) 49 | return .init(bytes: digest) 50 | } 51 | } 52 | 53 | public struct SHA384Digest: ByteDigest { 54 | public static var byteCount: Int { return Int(CC_SHA384_DIGEST_LENGTH) } 55 | public var bytes: [UInt8] 56 | } 57 | 58 | public struct SHA384: CCHashFunction { 59 | public typealias Digest = SHA384Digest 60 | public static var algorithm: CCHmacAlgorithm { return CCHmacAlgorithm(kCCHmacAlgSHA384) } 61 | var context: CC_SHA512_CTX 62 | 63 | public static func hash(bufferPointer: UnsafeRawBufferPointer) -> Self.Digest { 64 | var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount) 65 | CC_SHA384(bufferPointer.baseAddress, CC_LONG(bufferPointer.count), &digest) 66 | return .init(bytes: digest) 67 | } 68 | 69 | public init() { 70 | self.context = CC_SHA512_CTX() 71 | CC_SHA384_Init(&self.context) 72 | } 73 | 74 | public mutating func update(bufferPointer: UnsafeRawBufferPointer) { 75 | CC_SHA384_Update(&self.context, bufferPointer.baseAddress, CC_LONG(bufferPointer.count)) 76 | } 77 | 78 | public mutating func finalize() -> Self.Digest { 79 | var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount) 80 | CC_SHA384_Final(&digest, &self.context) 81 | return .init(bytes: digest) 82 | } 83 | } 84 | 85 | public struct SHA512Digest: ByteDigest { 86 | public static var byteCount: Int { return Int(CC_SHA512_DIGEST_LENGTH) } 87 | public var bytes: [UInt8] 88 | } 89 | 90 | public struct SHA512: CCHashFunction { 91 | public typealias Digest = SHA512Digest 92 | public static var algorithm: CCHmacAlgorithm { return CCHmacAlgorithm(kCCHmacAlgSHA512) } 93 | var context: CC_SHA512_CTX 94 | 95 | public static func hash(bufferPointer: UnsafeRawBufferPointer) -> Self.Digest { 96 | var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount) 97 | CC_SHA512(bufferPointer.baseAddress, CC_LONG(bufferPointer.count), &digest) 98 | return .init(bytes: digest) 99 | } 100 | 101 | public init() { 102 | self.context = CC_SHA512_CTX() 103 | CC_SHA512_Init(&self.context) 104 | } 105 | 106 | public mutating func update(bufferPointer: UnsafeRawBufferPointer) { 107 | CC_SHA512_Update(&self.context, bufferPointer.baseAddress, CC_LONG(bufferPointer.count)) 108 | } 109 | 110 | public mutating func finalize() -> Self.Digest { 111 | var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount) 112 | CC_SHA512_Final(&digest, &self.context) 113 | return .init(bytes: digest) 114 | } 115 | } 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/internal.h: -------------------------------------------------------------------------------- 1 | /* internal.h 2 | 3 | Internal definitions used by Expat. This is not needed to compile 4 | client code. 5 | 6 | The following calling convention macros are defined for frequently 7 | called functions: 8 | 9 | FASTCALL - Used for those internal functions that have a simple 10 | body and a low number of arguments and local variables. 11 | 12 | PTRCALL - Used for functions called though function pointers. 13 | 14 | PTRFASTCALL - Like PTRCALL, but for low number of arguments. 15 | 16 | inline - Used for selected internal functions for which inlining 17 | may improve performance on some platforms. 18 | 19 | Note: Use of these macros is based on judgement, not hard rules, 20 | and therefore subject to change. 21 | __ __ _ 22 | ___\ \/ /_ __ __ _| |_ 23 | / _ \\ /| '_ \ / _` | __| 24 | | __// \| |_) | (_| | |_ 25 | \___/_/\_\ .__/ \__,_|\__| 26 | |_| XML parser 27 | 28 | Copyright (c) 1997-2000 Thai Open Source Software Center Ltd 29 | Copyright (c) 2000-2017 Expat development team 30 | Licensed under the MIT license: 31 | 32 | Permission is hereby granted, free of charge, to any person obtaining 33 | a copy of this software and associated documentation files (the 34 | "Software"), to deal in the Software without restriction, including 35 | without limitation the rights to use, copy, modify, merge, publish, 36 | distribute, sublicense, and/or sell copies of the Software, and to permit 37 | persons to whom the Software is furnished to do so, subject to the 38 | following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included 41 | in all copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 46 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 47 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 48 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 49 | USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | #if defined(__GNUC__) && defined(__i386__) && ! defined(__MINGW32__) 53 | /* We'll use this version by default only where we know it helps. 54 | 55 | regparm() generates warnings on Solaris boxes. See SF bug #692878. 56 | 57 | Instability reported with egcs on a RedHat Linux 7.3. 58 | Let's comment out: 59 | #define FASTCALL __attribute__((stdcall, regparm(3))) 60 | and let's try this: 61 | */ 62 | # define FASTCALL __attribute__((regparm(3))) 63 | # define PTRFASTCALL __attribute__((regparm(3))) 64 | #endif 65 | 66 | /* Using __fastcall seems to have an unexpected negative effect under 67 | MS VC++, especially for function pointers, so we won't use it for 68 | now on that platform. It may be reconsidered for a future release 69 | if it can be made more effective. 70 | Likely reason: __fastcall on Windows is like stdcall, therefore 71 | the compiler cannot perform stack optimizations for call clusters. 72 | */ 73 | 74 | /* Make sure all of these are defined if they aren't already. */ 75 | 76 | #ifndef FASTCALL 77 | # define FASTCALL 78 | #endif 79 | 80 | #ifndef PTRCALL 81 | # define PTRCALL 82 | #endif 83 | 84 | #ifndef PTRFASTCALL 85 | # define PTRFASTCALL 86 | #endif 87 | 88 | #ifndef XML_MIN_SIZE 89 | # if ! defined(__cplusplus) && ! defined(inline) 90 | # ifdef __GNUC__ 91 | # define inline __inline 92 | # endif /* __GNUC__ */ 93 | # endif 94 | #endif /* XML_MIN_SIZE */ 95 | 96 | #ifdef __cplusplus 97 | # define inline inline 98 | #else 99 | # ifndef inline 100 | # define inline 101 | # endif 102 | #endif 103 | 104 | #ifndef UNUSED_P 105 | # define UNUSED_P(p) (void)p 106 | #endif 107 | 108 | #ifdef __cplusplus 109 | extern "C" { 110 | #endif 111 | 112 | #ifdef XML_ENABLE_VISIBILITY 113 | # if XML_ENABLE_VISIBILITY 114 | __attribute__((visibility("default"))) 115 | # endif 116 | #endif 117 | void 118 | _INTERNAL_trim_to_complete_utf8_characters(const char *from, 119 | const char **fromLimRef); 120 | 121 | #ifdef __cplusplus 122 | } 123 | #endif 124 | -------------------------------------------------------------------------------- /Sources/SotoCore/Credential/RotatingCredentialProvider.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import struct Foundation.TimeInterval 16 | import Logging 17 | import NIO 18 | import NIOConcurrencyHelpers 19 | import SotoSignerV4 20 | 21 | /// Used for wrapping another credential provider whose `getCredential` method returns an `ExpiringCredential`. 22 | /// If no credential is available, or the current credentials are going to expire in the near future the wrapped credential provider 23 | /// `getCredential` is called. If current credentials have not expired they are returned otherwise we wait on new 24 | /// credentials being provided. 25 | public final class RotatingCredentialProvider: CredentialProvider { 26 | let remainingTokenLifetimeForUse: TimeInterval 27 | 28 | public let provider: CredentialProvider 29 | private let lock = NIOConcurrencyHelpers.Lock() 30 | private var credential: Credential? 31 | private var credentialFuture: EventLoopFuture? 32 | 33 | public init(context: CredentialProviderFactory.Context, provider: CredentialProvider, remainingTokenLifetimeForUse: TimeInterval? = nil) { 34 | self.provider = provider 35 | self.remainingTokenLifetimeForUse = remainingTokenLifetimeForUse ?? 3 * 60 36 | _ = refreshCredentials(on: context.eventLoop, logger: context.logger) 37 | } 38 | 39 | /// Shutdown credential provider 40 | public func shutdown(on eventLoop: EventLoop) -> EventLoopFuture { 41 | return self.lock.withLock { 42 | if let future = credentialFuture { 43 | return future.and(provider.shutdown(on: eventLoop)).map { _ in }.hop(to: eventLoop) 44 | } 45 | return provider.shutdown(on: eventLoop) 46 | } 47 | } 48 | 49 | public func getCredential(on eventLoop: EventLoop, logger: Logger) -> EventLoopFuture { 50 | self.lock.lock() 51 | let cred = credential 52 | self.lock.unlock() 53 | 54 | switch cred { 55 | case .none: 56 | return self.refreshCredentials(on: eventLoop, logger: logger) 57 | case .some(let cred as ExpiringCredential): 58 | if cred.isExpiring(within: remainingTokenLifetimeForUse) { 59 | // the credentials are expiring... let's refresh 60 | return self.refreshCredentials(on: eventLoop, logger: logger) 61 | } 62 | 63 | return eventLoop.makeSucceededFuture(cred) 64 | case .some(let cred): 65 | // we don't have expiring credentials 66 | return eventLoop.makeSucceededFuture(cred) 67 | } 68 | } 69 | 70 | private func refreshCredentials(on eventLoop: EventLoop, logger: Logger) -> EventLoopFuture { 71 | self.lock.lock() 72 | defer { self.lock.unlock() } 73 | 74 | if let future = credentialFuture { 75 | // a refresh is already running 76 | if future.eventLoop !== eventLoop { 77 | // We want to hop back to the event loop we came in case 78 | // the refresh is resolved on another EventLoop. 79 | return future.hop(to: eventLoop) 80 | } 81 | return future 82 | } 83 | 84 | logger.debug("Refeshing AWS credentials", metadata: ["aws-credential-provider": .string("\(self)")]) 85 | 86 | credentialFuture = self.provider.getCredential(on: eventLoop, logger: logger) 87 | .map { (credential) -> (Credential) in 88 | // update the internal credential locked 89 | self.lock.withLock { 90 | self.credentialFuture = nil 91 | self.credential = credential 92 | logger.debug("AWS credentials ready", metadata: ["aws-credential-provider": .string("\(self)")]) 93 | } 94 | return credential 95 | } 96 | 97 | return credentialFuture! 98 | } 99 | } 100 | 101 | extension RotatingCredentialProvider: CustomStringConvertible { 102 | public var description: String { return "\(type(of: self))(\(provider.description))" } 103 | } 104 | -------------------------------------------------------------------------------- /Sources/SotoCore/Message/AWSMiddleware.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Logging 16 | import NIOHTTP1 17 | 18 | /// Context object sent to `AWSServiceMiddleware` `chain` functions 19 | public struct AWSMiddlewareContext { 20 | public let options: AWSServiceConfig.Options 21 | } 22 | 23 | /// Middleware protocol. Gives ability to process requests before they are sent to AWS and process responses before they are converted into output shapes 24 | public protocol AWSServiceMiddleware { 25 | /// Process AWSRequest before it is converted to a HTTPClient Request to be sent to AWS 26 | func chain(request: AWSRequest, context: AWSMiddlewareContext) throws -> AWSRequest 27 | 28 | /// Process response before it is converted to an output AWSShape 29 | func chain(response: AWSResponse, context: AWSMiddlewareContext) throws -> AWSResponse 30 | } 31 | 32 | /// Default versions of protocol functions 33 | public extension AWSServiceMiddleware { 34 | func chain(request: AWSRequest, context: AWSMiddlewareContext) throws -> AWSRequest { 35 | return request 36 | } 37 | 38 | func chain(response: AWSResponse, context: AWSMiddlewareContext) throws -> AWSResponse { 39 | return response 40 | } 41 | } 42 | 43 | /// Middleware struct that outputs the contents of requests being sent to AWS and the bodies of the responses received 44 | public struct AWSLoggingMiddleware: AWSServiceMiddleware { 45 | /// initialize AWSLoggingMiddleware 46 | /// - parameters: 47 | /// - log: Function to call with logging output 48 | public init(log: @escaping (String) -> Void = { print($0) }) { 49 | self.log = { log($0()) } 50 | } 51 | 52 | /// initialize AWSLoggingMiddleware to use Logger 53 | /// - Parameters: 54 | /// - logger: Logger to use 55 | /// - logLevel: Log level to output at 56 | public init(logger: Logger, logLevel: Logger.Level = .info) { 57 | self.log = { logger.log(level: logLevel, "\($0())") } 58 | } 59 | 60 | func getBodyOutput(_ body: Body) -> String { 61 | var output = "" 62 | switch body { 63 | case .xml(let element): 64 | output += "\n " 65 | output += element.description 66 | case .json(let buffer): 67 | output += "\n " 68 | output += buffer.getString(at: buffer.readerIndex, length: buffer.readableBytes) ?? "Failed to convert JSON response to UTF8" 69 | case .raw(let payload): 70 | output += "raw (\(payload.size?.description ?? "unknown") bytes)" 71 | case .text(let string): 72 | output += "\n \(string)" 73 | case .empty: 74 | output += "empty" 75 | } 76 | return output 77 | } 78 | 79 | func getHeadersOutput(_ headers: HTTPHeaders) -> String { 80 | if headers.count == 0 { 81 | return "[]" 82 | } 83 | var output = "[" 84 | for header in headers { 85 | output += "\n \(header.name) : \(header.value)" 86 | } 87 | return output + "\n ]" 88 | } 89 | 90 | /// output request 91 | public func chain(request: AWSRequest, context: AWSMiddlewareContext) throws -> AWSRequest { 92 | self.log( 93 | "Request:\n" + 94 | " \(request.operation)\n" + 95 | " \(request.httpMethod) \(request.url)\n" + 96 | " Headers: \(self.getHeadersOutput(request.httpHeaders))\n" + 97 | " Body: \(self.getBodyOutput(request.body))" 98 | ) 99 | return request 100 | } 101 | 102 | /// output response 103 | public func chain(response: AWSResponse, context: AWSMiddlewareContext) throws -> AWSResponse { 104 | self.log( 105 | "Response:\n" + 106 | " Status : \(response.status.code)\n" + 107 | " Headers: \(self.getHeadersOutput(HTTPHeaders(response.headers.map { ($0, "\($1)") })))\n" + 108 | " Body: \(self.getBodyOutput(response.body))" 109 | ) 110 | return response 111 | } 112 | 113 | let log: (@autoclosure () -> String) -> Void 114 | } 115 | -------------------------------------------------------------------------------- /Sources/CSotoExpat/xmltok_ns.c: -------------------------------------------------------------------------------- 1 | /* This file is included! 2 | __ __ _ 3 | ___\ \/ /_ __ __ _| |_ 4 | / _ \\ /| '_ \ / _` | __| 5 | | __// \| |_) | (_| | |_ 6 | \___/_/\_\ .__/ \__,_|\__| 7 | |_| XML parser 8 | 9 | Copyright (c) 1997-2000 Thai Open Source Software Center Ltd 10 | Copyright (c) 2000-2017 Expat development team 11 | Licensed under the MIT license: 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining 14 | a copy of this software and associated documentation files (the 15 | "Software"), to deal in the Software without restriction, including 16 | without limitation the rights to use, copy, modify, merge, publish, 17 | distribute, sublicense, and/or sell copies of the Software, and to permit 18 | persons to whom the Software is furnished to do so, subject to the 19 | following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included 22 | in all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 27 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 28 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 29 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 30 | USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | #ifdef XML_TOK_NS_C 34 | 35 | const ENCODING * 36 | NS(XmlGetUtf8InternalEncoding)(void) { 37 | return &ns(internal_utf8_encoding).enc; 38 | } 39 | 40 | const ENCODING * 41 | NS(XmlGetUtf16InternalEncoding)(void) { 42 | # if BYTEORDER == 1234 43 | return &ns(internal_little2_encoding).enc; 44 | # elif BYTEORDER == 4321 45 | return &ns(internal_big2_encoding).enc; 46 | # else 47 | const short n = 1; 48 | return (*(const char *)&n ? &ns(internal_little2_encoding).enc 49 | : &ns(internal_big2_encoding).enc); 50 | # endif 51 | } 52 | 53 | static const ENCODING *const NS(encodings)[] = { 54 | &ns(latin1_encoding).enc, &ns(ascii_encoding).enc, 55 | &ns(utf8_encoding).enc, &ns(big2_encoding).enc, 56 | &ns(big2_encoding).enc, &ns(little2_encoding).enc, 57 | &ns(utf8_encoding).enc /* NO_ENC */ 58 | }; 59 | 60 | static int PTRCALL 61 | NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, 62 | const char **nextTokPtr) { 63 | return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_PROLOG_STATE, 64 | ptr, end, nextTokPtr); 65 | } 66 | 67 | static int PTRCALL 68 | NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, 69 | const char **nextTokPtr) { 70 | return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_CONTENT_STATE, 71 | ptr, end, nextTokPtr); 72 | } 73 | 74 | int 75 | NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, 76 | const char *name) { 77 | int i = getEncodingIndex(name); 78 | if (i == UNKNOWN_ENC) 79 | return 0; 80 | SET_INIT_ENC_INDEX(p, i); 81 | p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog); 82 | p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent); 83 | p->initEnc.updatePosition = initUpdatePosition; 84 | p->encPtr = encPtr; 85 | *encPtr = &(p->initEnc); 86 | return 1; 87 | } 88 | 89 | static const ENCODING * 90 | NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) { 91 | # define ENCODING_MAX 128 92 | char buf[ENCODING_MAX]; 93 | char *p = buf; 94 | int i; 95 | XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); 96 | if (ptr != end) 97 | return 0; 98 | *p = 0; 99 | if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) 100 | return enc; 101 | i = getEncodingIndex(buf); 102 | if (i == UNKNOWN_ENC) 103 | return 0; 104 | return NS(encodings)[i]; 105 | } 106 | 107 | int 108 | NS(XmlParseXmlDecl)(int isGeneralTextEntity, const ENCODING *enc, 109 | const char *ptr, const char *end, const char **badPtr, 110 | const char **versionPtr, const char **versionEndPtr, 111 | const char **encodingName, const ENCODING **encoding, 112 | int *standalone) { 113 | return doParseXmlDecl(NS(findEncoding), isGeneralTextEntity, enc, ptr, end, 114 | badPtr, versionPtr, versionEndPtr, encodingName, 115 | encoding, standalone); 116 | } 117 | 118 | #endif /* XML_TOK_NS_C */ 119 | -------------------------------------------------------------------------------- /Sources/SotoCore/Credential/ConfigFileCredentialProvider.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | import Logging 16 | import NIO 17 | import NIOConcurrencyHelpers 18 | import SotoSignerV4 19 | 20 | class ConfigFileCredentialProvider: CredentialProviderSelector { 21 | /// promise to find a credential provider 22 | let startupPromise: EventLoopPromise 23 | /// lock for access to _internalProvider. 24 | let lock = Lock() 25 | /// internal version of internal provider. Should access this through `internalProvider` 26 | var _internalProvider: CredentialProvider? 27 | 28 | init( 29 | credentialsFilePath: String, 30 | configFilePath: String, 31 | profile: String? = nil, 32 | context: CredentialProviderFactory.Context, 33 | endpoint: String? = nil 34 | ) { 35 | self.startupPromise = context.eventLoop.makePromise(of: CredentialProvider.self) 36 | self.startupPromise.futureResult.whenSuccess { result in 37 | self.internalProvider = result 38 | } 39 | 40 | let profile = profile ?? Environment["AWS_PROFILE"] ?? ConfigFile.defaultProfile 41 | Self.credentialProvider(from: credentialsFilePath, configFilePath: configFilePath, for: profile, context: context, endpoint: endpoint) 42 | .cascade(to: self.startupPromise) 43 | } 44 | 45 | /// Credential provider from shared credentials and profile configuration files 46 | /// 47 | /// - Parameters: 48 | /// - credentialsByteBuffer: contents of AWS shared credentials file (usually `~/.aws/credentials`) 49 | /// - configByteBuffer: contents of AWS profile configuration file (usually `~/.aws/config`) 50 | /// - profile: named profile to load (usually `default`) 51 | /// - context: credential provider factory context 52 | /// - endpoint: STS Assume role endpoint (for unit testing) 53 | /// - Returns: Credential Provider (StaticCredentials or STSAssumeRole) 54 | static func credentialProvider( 55 | from credentialsFilePath: String, 56 | configFilePath: String, 57 | for profile: String, 58 | context: CredentialProviderFactory.Context, 59 | endpoint: String? 60 | ) -> EventLoopFuture { 61 | return ConfigFileLoader.loadSharedCredentials( 62 | credentialsFilePath: credentialsFilePath, 63 | configFilePath: configFilePath, 64 | profile: profile, 65 | context: context 66 | ) 67 | .flatMapThrowing { sharedCredentials in 68 | return try credentialProvider(from: sharedCredentials, context: context, endpoint: endpoint) 69 | } 70 | } 71 | 72 | /// Generate credential provider based on shared credentials and profile configuration 73 | /// 74 | /// Credentials file settings have precedence over profile configuration settings 75 | /// https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-precedence 76 | /// 77 | /// - Parameters: 78 | /// - sharedCredentials: combined credentials loaded from disl (usually `~/.aws/credentials` and `~/.aws/config`) 79 | /// - context: credential provider factory context 80 | /// - endpoint: STS Assume role endpoint (for unit testing) 81 | /// - Returns: Credential Provider (StaticCredentials or STSAssumeRole) 82 | static func credentialProvider( 83 | from sharedCredentials: ConfigFileLoader.SharedCredentials, 84 | context: CredentialProviderFactory.Context, 85 | endpoint: String? 86 | ) throws -> CredentialProvider { 87 | switch sharedCredentials { 88 | case .staticCredential(let staticCredential): 89 | return staticCredential 90 | case .assumeRole(let roleArn, let sessionName, let region, let sourceCredentialProvider): 91 | let request = STSAssumeRoleRequest(roleArn: roleArn, roleSessionName: sessionName) 92 | let region = region ?? .useast1 93 | return STSAssumeRoleCredentialProvider( 94 | request: request, 95 | credentialProvider: sourceCredentialProvider, 96 | region: region, 97 | httpClient: context.httpClient, 98 | endpoint: endpoint 99 | ) 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Tests/SotoCoreTests/AWSServiceTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Soto for AWS open source project 4 | // 5 | // Copyright (c) 2017-2020 the Soto project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // See CONTRIBUTORS.txt for the list of Soto project authors 10 | // 11 | // SPDX-License-Identifier: Apache-2.0 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | @testable import SotoCore 16 | import SotoTestUtils 17 | import XCTest 18 | 19 | class AWSServiceTests: XCTestCase { 20 | struct TestService: AWSService { 21 | var client: AWSClient 22 | var config: AWSServiceConfig 23 | 24 | /// init 25 | init(client: AWSClient, config: AWSServiceConfig) { 26 | self.client = client 27 | self.config = config 28 | } 29 | 30 | /// patch init 31 | init(from: Self, patch: AWSServiceConfig.Patch) { 32 | self.client = from.client 33 | self.config = from.config.with(patch: patch) 34 | } 35 | } 36 | 37 | func testRegion() { 38 | let client = createAWSClient() 39 | defer { XCTAssertNoThrow(try client.syncShutdown()) } 40 | let serviceConfig = createServiceConfig(region: .apnortheast2) 41 | let service = TestService(client: client, config: serviceConfig) 42 | XCTAssertEqual(service.region, .apnortheast2) 43 | } 44 | 45 | func testEndpoint() { 46 | let client = createAWSClient() 47 | defer { XCTAssertNoThrow(try client.syncShutdown()) } 48 | let serviceConfig = createServiceConfig(endpoint: "https://my-endpoint.com") 49 | let service = TestService(client: client, config: serviceConfig) 50 | XCTAssertEqual(service.endpoint, "https://my-endpoint.com") 51 | } 52 | 53 | func testWith() { 54 | let client = createAWSClient() 55 | defer { XCTAssertNoThrow(try client.syncShutdown()) } 56 | let serviceConfig = createServiceConfig() 57 | let service = TestService(client: client, config: serviceConfig) 58 | let service2 = service.with(timeout: .seconds(2048), options: .init(rawValue: 0x67FF)) 59 | XCTAssertEqual(service2.config.timeout, .seconds(2048)) 60 | XCTAssertEqual(service2.config.options, .init(rawValue: 0x67FF)) 61 | } 62 | 63 | func testWithMiddleware() { 64 | struct TestMiddleware: AWSServiceMiddleware {} 65 | let client = createAWSClient() 66 | defer { XCTAssertNoThrow(try client.syncShutdown()) } 67 | let serviceConfig = createServiceConfig() 68 | let service = TestService(client: client, config: serviceConfig) 69 | let service2 = service.with(middlewares: [TestMiddleware()]) 70 | XCTAssertNotNil(service2.config.middlewares.first { type(of: $0) == TestMiddleware.self }) 71 | } 72 | 73 | func testSignURL() throws { 74 | let client = createAWSClient(credentialProvider: .static(accessKeyId: "foo", secretAccessKey: "bar")) 75 | defer { XCTAssertNoThrow(try client.syncShutdown()) } 76 | let serviceConfig = createServiceConfig() 77 | let service = TestService(client: client, config: serviceConfig) 78 | let url = URL(string: "https://test.amazonaws.com?test2=true&space=sp%20ace&percent=addi+tion")! 79 | let signedURL = try service.signURL(url: url, httpMethod: .GET, expires: .minutes(15)).wait() 80 | // remove signed query params 81 | let query = try XCTUnwrap(signedURL.query) 82 | let queryItems = query 83 | .split(separator: "&") 84 | .compactMap { 85 | guard !$0.hasPrefix("X-Amz") else { return nil } 86 | return String($0) 87 | } 88 | .joined(separator: "&") 89 | XCTAssertEqual(queryItems, "percent=addi%2Btion&space=sp%20ace&test2=true") 90 | } 91 | 92 | func testSignHeaders() throws { 93 | let client = createAWSClient(credentialProvider: .static(accessKeyId: "foo", secretAccessKey: "bar")) 94 | defer { XCTAssertNoThrow(try client.syncShutdown()) } 95 | let serviceConfig = createServiceConfig() 96 | let service = TestService(client: client, config: serviceConfig) 97 | let url = URL(string: "https://test.amazonaws.com?test2=true&space=sp%20ace&percent=addi+tion")! 98 | let headers = try service.signHeaders( 99 | url: url, 100 | httpMethod: .GET, 101 | headers: ["Content-Type": "application/json"], 102 | body: .string("Test payload") 103 | ).wait() 104 | // remove signed query params 105 | XCTAssertNotNil(headers["Authorization"].first) 106 | } 107 | } 108 | --------------------------------------------------------------------------------